Objek Class yang kita peroleh lewat literal, getClass(), atau Class.forName menyimpan peta lengkap sebuah tipe. Pertanyaan berikutnya: bagaimana cara membaca petanya? java.lang.reflect menyediakan method introspeksi untuk hampir semua properti struktural — nama lengkap dan sederhana, package, modifier akses, superclass, daftar interface, sampai identifikasi khusus seperti record, enum, dan array. Kemampuan ini adalah bekal dasar alat seperti generator kode, serializer, dan debugger yang harus memahami class tanpa membuka sumbernya. Pada Chapter ini kita membangun method inspeksi serbaguna lalu mengujinya pada enam jenis tipe berbeda.
Properti Struktural pada Objek Class #
Sebelum masuk ke kode, mari petakan method pembaca struktur yang paling sering dipakai:
| Method | Menghasilkan |
|---|---|
getName() | nama lengkap dengan package, misalnya java.lang.String |
getSimpleName() | nama tanpa package, misalnya String |
getPackageName() | nama package saja |
getModifiers() | bit modifier, diterjemahkan Modifier.toString(...) |
getSuperclass() | objek Class superclass, atau null untuk interface dan Object |
getInterfaces() | array interface yang dideklarasikan (bukan yang diwarisi superclass) |
isInterface(), isRecord(), isEnum(), isArray() | klasifikasi jenis tipe |
Dua detail penting sering mengejutkan pemula. Pertama, getSuperclass() mengembalikan null untuk interface — interface tidak punya superclass dalam hierarki class — dan java.lang.Object untuk class tanpa extends eksplisit. Kedua, getInterfaces() hanya melaporkan interface yang ditulis pada deklarasi implements class tersebut, bukan seluruh interface yang diwariskan bertingkat lewat superclass. Modifier sendiri tersimpan sebagai bit mask integer; method Modifier.toString() menerjemahkannya menjadi teks seperti public static final.
Inspeksi Enam Jenis Tipe #
Program berikut mendefinisikan method periksa yang mencetak seluruh properti di atas, lalu mengujinya pada interface, class abstrak, class konkret, record, enum, dan array:
import java.lang.reflect.*;
interface BisaTerbang {
void terbang();
}
abstract class Hewan {
protected String nama;
public abstract void bersuara();
}
class Burung extends Hewan implements BisaTerbang {
@Override
public void bersuara() {
System.out.println("Cipok!");
}
@Override
public void terbang() {
System.out.println("Terbang!");
}
}
record Titik(int x, int y) {}
enum Musim { KEMARAU, HUJAN }
public class InspeksiStruktur {
public static void main(String[] args) {
periksa("interface", BisaTerbang.class);
periksa("class abstrak", Hewan.class);
periksa("class konkret", Burung.class);
periksa("record", Titik.class);
periksa("enum", Musim.class);
periksa("array", int[].class);
}
static void periksa(String label, Class<?> c) {
System.out.println("== " + label + " : " + c.getName());
System.out.println(" sederhana : " + c.getSimpleName());
System.out.println(" package : '" + c.getPackageName() + "'");
String mod = Modifier.toString(c.getModifiers());
System.out.println(" modifier : " + (mod.isEmpty() ? "(package-private)" : mod));
Class<?> induk = c.getSuperclass();
System.out.println(" superclass: " + (induk == null ? "(tidak ada)" : induk.getSimpleName()));
System.out.println(" interfaces: " + ringkas(c.getInterfaces()));
System.out.println(" record? " + c.isRecord() + ", enum? " + c.isEnum()
+ ", array? " + c.isArray() + ", interface? " + c.isInterface());
}
static String ringkas(Class<?>[] daftar) {
if (daftar.length == 0) {
return "(tidak ada)";
}
StringBuilder hasil = new StringBuilder();
for (Class<?> i : daftar) {
if (hasil.length() > 0) {
hasil.append(", ");
}
hasil.append(i.getSimpleName());
}
return hasil.toString();
}
}Output yang Dihasilkan:
== interface : BisaTerbang
sederhana : BisaTerbang
package : ''
modifier : abstract interface
superclass: (tidak ada)
interfaces: (tidak ada)
record? false, enum? false, array? false, interface? true
== class abstrak : Hewan
sederhana : Hewan
package : ''
modifier : abstract
superclass: Object
interfaces: (tidak ada)
record? false, enum? false, array? false, interface? false
== class konkret : Burung
sederhana : Burung
package : ''
modifier : (package-private)
superclass: Hewan
interfaces: BisaTerbang
record? false, enum? false, array? false, interface? false
== record : Titik
sederhana : Titik
package : ''
modifier : final
superclass: Record
interfaces: (tidak ada)
record? true, enum? false, array? false, interface? false
== enum : Musim
sederhana : Musim
package : ''
modifier : final
superclass: Enum
interfaces: (tidak ada)
record? false, enum? true, array? false, interface? false
== array : [I
sederhana : int[]
package : 'java.lang'
modifier : public abstract final
superclass: Object
interfaces: Cloneable, Serializable
record? false, enum? false, array? true, interface? falseEnam blok hasil memperlihatkan karakter masing-masing jenis tipe. Interface BisaTerbang melaporkan modifier abstract interface — compiler selalu menambahkan kedua bit itu — dan superclass kosong. Class Burung menunjukkan modifier (package-private) karena tidak ada kata kunci akses pada deklarasinya; Modifier.toString() mengembalikan string kosong yang program kita ganti label agar mudah dibaca. Record Titik selalu final dengan superclass Record, enum Musim selalu final dengan superclass Enum, dan array int[] bahkan membawa modifier public abstract final sekaligus interface Cloneable dan Serializable ciptaan JVM.
Dua baris lain menarik dicermati. Baris nama array tercetak [I — encoding internal JVM untuk array int satu dimensi — sementara getSimpleName() menghasilkan teks int[] yang kita kenal. Perhatikan juga package array bernilai java.lang: array melaporkan package dari tipe komponennya, bukan package kosong, meskipun int sendiri adalah primitif tanpa package.
Melihat Anggota dengan Modifiernya #
Struktur class mencakup juga anggotanya. getDeclaredFields() dan getDeclaredMethods() mengembalikan seluruh field dan method yang dideklarasikan langsung pada class tersebut — termasuk yang private dan protected, yang tidak muncul lewat getFields()/getMethods() versi publik. Method getModifiers() pada tiap anggota bekerja sama dengan Modifier.toString() seperti pada level class:
import java.lang.reflect.*;
import java.util.Arrays;
import java.util.Comparator;
class Rekening {
public static final String BANK = "Bank Nusantara";
private long saldo;
protected String pemilik;
public Rekening(String pemilik, long saldo) {
this.pemilik = pemilik;
this.saldo = saldo;
}
public long bacaSaldo() {
return saldo;
}
private void geserSaldo(long delta) {
saldo += delta;
}
}
public class InspeksiAnggota {
public static void main(String[] args) {
Class<?> c = Rekening.class;
System.out.println("Field milik Rekening:");
Field[] field = c.getDeclaredFields();
Arrays.sort(field, Comparator.comparing(Field::getName));
for (Field f : field) {
System.out.println(" " + Modifier.toString(f.getModifiers()) + " "
+ f.getType().getSimpleName() + " " + f.getName());
}
System.out.println("Method milik Rekening:");
Method[] method = c.getDeclaredMethods();
Arrays.sort(method, Comparator.comparing(Method::getName));
for (Method m : method) {
System.out.println(" " + Modifier.toString(m.getModifiers()) + " "
+ m.getReturnType().getSimpleName() + " " + m.getName() + "()");
}
}
}Output yang Dihasilkan:
Field milik Rekening:
public static final String BANK
protected String pemilik
private long saldo
Method milik Rekening:
public long bacaSaldo()
private void geserSaldo()Mari kita telaah bagian-bagian penting kode di atas:
- Baris 34-36 (
getDeclaredFields()+Arrays.sort): mengambil tiga fieldRekeningtermasuk yangprivate. Urutan hasil tidak dijamin JLS, sehingga pengurutan per nama menjaga output deterministik di setiap eksekusi. - Baris 37-39 (
f.getType().getSimpleName()): tipe field juga objekClass, jadilongprimitif maupunStringbisa dibaca namanya —long saldodanString pemilikpada output berasal dari sini. - Baris 42-47 (
getDeclaredMethods()+ cetak): setiapMethodmelaporkan modifier, tipe kembalianm.getReturnType(), dan nama. MethodprivategeserSaldoikut terlihat — mendeklarasikanprivatetidak menyembunyikan anggota dari reflection, hanya dari pemanggilan langsung. - Konstruktor tidak ikut:
getDeclaredMethods()tidak memuat constructor; constructor diambil lewatgetDeclaredConstructors()tersendiri.
Method geserSaldo pada output tercetak lengkap dengan modifier private — reflection membacanya tanpa keberatan. Membaca nama anggota privat memang selalu boleh; yang diatur ketat adalah mengakses nilainya. Chapter berikutnya membahas batas itu: membaca dan mengubah field privat lewat setAccessible, memanggil method privat lewat invoke, beserta konsekuensi keamanannya.
Referensi #
- Class java.lang.Class (Oracle)
- Class java.lang.reflect.Modifier (Oracle)
- Package java.lang.reflect (Oracle)
- Dev.java: Learn Java
Berkas Sumber
Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.
InspeksiAnggota.java
Unduh berkasimport java.lang.reflect.*;
import java.util.Arrays;
import java.util.Comparator;
class Rekening {
public static final String BANK = "Bank Nusantara";
private long saldo;
protected String pemilik;
public Rekening(String pemilik, long saldo) {
this.pemilik = pemilik;
this.saldo = saldo;
}
public long bacaSaldo() {
return saldo;
}
private void geserSaldo(long delta) {
saldo += delta;
}
}
public class InspeksiAnggota {
public static void main(String[] args) {
Class<?> c = Rekening.class;
System.out.println("Field milik Rekening:");
Field[] field = c.getDeclaredFields();
Arrays.sort(field, Comparator.comparing(Field::getName));
for (Field f : field) {
System.out.println(" " + Modifier.toString(f.getModifiers()) + " "
+ f.getType().getSimpleName() + " " + f.getName());
}
System.out.println("Method milik Rekening:");
Method[] method = c.getDeclaredMethods();
Arrays.sort(method, Comparator.comparing(Method::getName));
for (Method m : method) {
System.out.println(" " + Modifier.toString(m.getModifiers()) + " "
+ m.getReturnType().getSimpleName() + " " + m.getName() + "()");
}
}
}
InspeksiStruktur.java
Unduh berkasimport java.lang.reflect.*;
interface BisaTerbang {
void terbang();
}
abstract class Hewan {
protected String nama;
public abstract void bersuara();
}
class Burung extends Hewan implements BisaTerbang {
@Override
public void bersuara() {
System.out.println("Cipok!");
}
@Override
public void terbang() {
System.out.println("Terbang!");
}
}
record Titik(int x, int y) {}
enum Musim { KEMARAU, HUJAN }
public class InspeksiStruktur {
public static void main(String[] args) {
periksa("interface", BisaTerbang.class);
periksa("class abstrak", Hewan.class);
periksa("class konkret", Burung.class);
periksa("record", Titik.class);
periksa("enum", Musim.class);
periksa("array", int[].class);
}
static void periksa(String label, Class<?> c) {
System.out.println("== " + label + " : " + c.getName());
System.out.println(" sederhana : " + c.getSimpleName());
System.out.println(" package : '" + c.getPackageName() + "'");
String mod = Modifier.toString(c.getModifiers());
System.out.println(" modifier : " + (mod.isEmpty() ? "(package-private)" : mod));
Class<?> induk = c.getSuperclass();
System.out.println(" superclass: " + (induk == null ? "(tidak ada)" : induk.getSimpleName()));
System.out.println(" interfaces: " + ringkas(c.getInterfaces()));
System.out.println(" record? " + c.isRecord() + ", enum? " + c.isEnum()
+ ", array? " + c.isArray() + ", interface? " + c.isInterface());
}
static String ringkas(Class<?>[] daftar) {
if (daftar.length == 0) {
return "(tidak ada)";
}
StringBuilder hasil = new StringBuilder();
for (Class<?> i : daftar) {
if (hasil.length() > 0) {
hasil.append(", ");
}
hasil.append(i.getSimpleName());
}
return hasil.toString();
}
}