Konstruk Sintetik di Jawa

1. Gambaran keseluruhan

Dalam tutorial ini, kita akan melihat konstruk sintetik Java, kod yang diperkenalkan oleh penyusun untuk mengendalikan akses kepada ahli secara telus yang sebaliknya tidak dapat dicapai kerana penglihatan yang tidak mencukupi atau rujukan yang hilang.

Catatan: bermula dengan JDK 11, kaedah dan konstruktor sintetik tidak lagi dihasilkan, kerana digantikan oleh kawalan akses berasaskan sarang.

2. Sintetik di Jawa

Definisi terbaik sintetik yang mungkin kita dapati datang langsung dari Spesifikasi Bahasa Java (JLS 13.1.7):

Segala konstruk yang diperkenalkan oleh penyusun Java yang tidak mempunyai konstruk yang sesuai dalam kod sumber mesti ditandakan sebagai sintetik, kecuali untuk konstruktor lalai, kaedah inisialisasi kelas, dan nilai dan nilai dari kaedah Enum kelas.

Terdapat pelbagai jenis konstruksi penyusunan, iaitu bidang, konstruktor, dan kaedah. Sebaliknya, walaupun kelas bersarang dapat diubah oleh penyusun (iaitu kelas tanpa nama), mereka tidak dianggap sintetik .

Tanpa basa-basi lagi, mari kita bahas setiap perkara ini.

3. Medan Sintetik

Mari mulakan dengan kelas bersarang sederhana:

public class SyntheticFieldDemo { class NestedClass {} }

Apabila disusun, mana-mana kelas dalaman akan mengandungi medan sintetikyang merujuk kepada kelas atasan. Secara kebetulan, inilah yang memungkinkan untuk mengakses ahli kelas yang dilampirkan dari kelas bersarang.

Untuk memastikan bahawa inilah yang berlaku, kami akan melaksanakan ujian yang mendapat medan kelas bersarang secara refleksi dan memeriksanya menggunakan kaedah 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 untuk mengesahkan ini adalah dengan menjalankan disassembler melalui perintah javap. Dalam kedua-dua kes, output menunjukkan medan sintetik bernama $ 0 ini.

4. Kaedah Sintetik

Seterusnya, kami akan menambah medan peribadi 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 kes ini, penyusunan akan menghasilkan aksesor ke pemboleh ubah. Tanpa kaedah ini, mustahil untuk mengakses medan peribadi dari contoh lampiran.

Sekali lagi, kita dapat memeriksanya dengan teknik yang sama yang menunjukkan dua kaedah 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 bahawa untuk menghasilkan kod, bidang mesti dibaca dari atau ditulis ke , jika tidak, kaedahnya akan dioptimumkan . Ini adalah sebab mengapa kami juga menambah getter dan setter.

Seperti yang dinyatakan di atas, kaedah sintetik ini tidak lagi dihasilkan bermula dengan JDK 11.

4.1. Kaedah Jambatan

Kaedah khas kaedah sintetik adalah kaedah jambatan, yang menangani penghapusan jenis generik.

Sebagai contoh, mari kita pertimbangkan Pembanding sederhana :

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

Walaupun membandingkan () mengambil dua argumen Integer dalam sumbernya, setelah disusun, ia akan mengambil dua argumen Objek sebagai gantinya, kerana penghapusan jenis.

Untuk menguruskannya, penyusun membuat jambatan sintetik yang menguruskan hujah :

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

Sebagai tambahan kepada ujian sebelumnya, kali ini kami juga akan memanggil isBridge () dari kelas Kaedah :

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. Pembina Sintetik

Akhirnya, kami akan menambah pembina peribadi:

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

Kali ini, setelah kita menjalankan ujian atau pembongkaran, kita akan melihat bahawa sebenarnya ada dua pembina, salah satunya adalah 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 seperti medan sintetik, konstruktor yang dihasilkan ini sangat mustahak untuk mewujudkan kelas bersarang dengan konstruktor persendirian dari contohnya.

Seperti yang dinyatakan di atas, konstruktor sintetik tidak lagi dihasilkan bermula dengan JDK 11.

6. Kesimpulannya

Dalam artikel ini, kami membincangkan konstruk sintetik yang dihasilkan oleh penyusun Java. Untuk mengujinya, kami menggunakan refleksi, yang boleh anda ketahui lebih lanjut di sini.

Seperti biasa, semua kod boleh didapati di GitHub.