Konstruksi Sintetis di Java

1. Ikhtisar

Dalam tutorial ini, kita akan melihat konstruksi sintetis Java, kode yang diperkenalkan oleh compiler untuk secara transparan menangani akses ke anggota yang tidak bisa dijangkau karena visibilitas yang tidak memadai atau referensi yang hilang.

Catatan: dimulai dengan JDK 11, metode dan konstruktor sintetik tidak lagi dibuat, karena digantikan oleh kontrol akses berbasis sarang.

2. Sintetis di Jawa

Definisi sintetik terbaik yang mungkin dapat kami temukan berasal langsung dari Spesifikasi Bahasa Java (JLS 13.1.7):

Setiap konstruksi yang diperkenalkan oleh compiler Java yang tidak memiliki konstruksi yang sesuai dalam kode sumber harus ditandai sebagai sintetik, kecuali untuk konstruktor default, metode inisialisasi kelas, dan nilai serta metode valueOf dari kelas Enum.

Ada berbagai macam konstruksi kompilasi, yaitu field, konstruktor, dan metode. Di sisi lain, meskipun kelas bersarang dapat diubah oleh kompilator (yaitu kelas anonim), kelas tersebut tidak dianggap sintetis .

Tanpa basa-basi lagi, mari selami masing-masing hal ini.

3. Bidang Sintetis

Mari kita mulai dengan kelas bersarang sederhana:

public class SyntheticFieldDemo { class NestedClass {} }

Saat dikompilasi, kelas dalam apa pun akan berisi bidang sintetisyang mereferensikan kelas tingkat atas. Secara kebetulan, inilah yang memungkinkan untuk mengakses anggota kelas yang melingkupi dari kelas bersarang.

Untuk memastikan bahwa inilah yang terjadi, kita akan mengimplementasikan pengujian yang mendapatkan bidang kelas bersarang dengan refleksi dan memeriksanya menggunakan metode isSynthetic () :

public void givenSyntheticField_whenIsSynthetic_thenTrue() { Field[] fields = SyntheticFieldDemo.NestedClass.class .getDeclaredFields(); assertEquals("This class should contain only one field", 1, fields.length); for (Field f : fields) { System.out.println("Field: " + f.getName() + ", isSynthetic: " + f.isSynthetic()); assertTrue("All the fields of this class should be synthetic", f.isSynthetic()); } }

Cara lain kami dapat memverifikasi ini adalah dengan menjalankan disassembler melalui perintah javap. Dalam kedua kasus tersebut, keluarannya menunjukkan bidang sintetis bernama $ 0 ini.

4. Metode Sintetis

Selanjutnya, kami akan menambahkan bidang pribadi ke kelas bersarang kami:

public class SyntheticMethodDemo { class NestedClass { private String nestedField; } public String getNestedField() { return new NestedClass().nestedField; } public void setNestedField(String nestedField) { new NestedClass().nestedField = nestedField; } }

Dalam kasus ini, kompilasi akan menghasilkan pengakses ke variabel. Tanpa metode ini, tidak mungkin untuk mengakses bidang pribadi dari contoh yang melampirkan.

Sekali lagi, kita dapat memeriksa ini dengan teknik yang sama yang menunjukkan dua metode sintetik yang disebut akses $ 0 dan akses $ 1 :

public void givenSyntheticMethod_whenIsSynthetic_thenTrue() { Method[] methods = SyntheticMethodDemo.NestedClass.class .getDeclaredMethods(); assertEquals("This class should contain only two methods", 2, methods.length); for (Method m : methods) { System.out.println("Method: " + m.getName() + ", isSynthetic: " + m.isSynthetic()); assertTrue("All the methods of this class should be synthetic", m.isSynthetic()); } }

Perhatikan bahwa untuk menghasilkan kode, bidang harus benar-benar dibaca dari atau ditulis , jika tidak, metode akan dioptimalkan . Inilah alasan mengapa kami juga menambahkan getter dan setter.

Seperti disebutkan di atas, metode sintetis ini tidak lagi dibuat mulai dengan JDK 11.

4.1. Metode Jembatan

Kasus khusus dari metode sintetik adalah metode jembatan, yang menangani penghapusan jenis obat generik.

Misalnya, mari pertimbangkan Pembanding sederhana :

public class BridgeMethodDemo implements Comparator { @Override public int compare(Integer o1, Integer o2) { return 0; } }

Meskipun bandingkan () mengambil dua argumen Integer dalam sumbernya, setelah dikompilasi itu akan mengambil dua argumen Object sebagai gantinya, karena tipe erasure.

Untuk mengelolanya, compiler membuat jembatan sintetis yang menangani transmisi argumen :

public int compare(Object o1, Object o2) { return compare((Integer) o1, (Integer) o2); }

Selain pengujian kami sebelumnya, kali ini kami juga akan memanggil isBridge () dari kelas Metode :

public void givenBridgeMethod_whenIsBridge_thenTrue() { int syntheticMethods = 0; Method[] methods = BridgeMethodDemo.class.getDeclaredMethods(); for (Method m : methods) { System.out.println("Method: " + m.getName() + ", isSynthetic: " + m.isSynthetic() + ", isBridge: " + m.isBridge()); if (m.isSynthetic()) { syntheticMethods++; assertTrue("The synthetic method in this class should also be a bridge method", m.isBridge()); } } assertEquals("There should be exactly 1 synthetic bridge method in this class", 1, syntheticMethods); }

5. Pembangun Sintetis

Akhirnya, kami akan menambahkan konstruktor pribadi:

public class SyntheticConstructorDemo { private NestedClass nestedClass = new NestedClass(); class NestedClass { private NestedClass() {} } }

Kali ini, setelah kita menjalankan tes atau disassembler kita akan melihat bahwa sebenarnya ada dua konstruktor, salah satunya sintetik:

public void givenSyntheticConstructor_whenIsSynthetic_thenTrue() { int syntheticConstructors = 0; Constructor[] constructors = SyntheticConstructorDemo.NestedClass .class.getDeclaredConstructors(); assertEquals("This class should contain only two constructors", 2, constructors.length); for (Constructor c : constructors) { System.out.println("Constructor: " + c.getName() + ", isSynthetic: " + c.isSynthetic()); if (c.isSynthetic()) { syntheticConstructors++; } } assertEquals(1, syntheticConstructors); }

Sama halnya dengan bidang sintetik, konstruktor yang dihasilkan ini penting untuk membuat instance kelas bersarang dengan konstruktor privat dari instance yang melingkupinya.

Seperti disebutkan di atas, konstruktor sintetis tidak lagi dibuat mulai dengan JDK 11.

6. Kesimpulan

Pada artikel ini, kita membahas konstruksi sintetis yang dihasilkan oleh compiler Java. Untuk mengujinya, kami memanfaatkan refleksi, yang dapat Anda pelajari lebih lanjut di sini.

Seperti biasa, semua kode tersedia di GitHub.