Kaedah Sintetik Jawa

Dalam catatan blog ini, saya melihat konsep kaedah sintetik Java. Posting tersebut merangkum apa itu metode sintetik Java, bagaimana seseorang dapat dibuat dan dikenal pasti, dan implikasi kaedah sintetik Java terhadap pengembangan Java.

Spesifikasi Bahasa Java (bahagian 13.1) menyatakan "Setiap konstruk yang diperkenalkan oleh penyusun yang tidak mempunyai konstruk yang sesuai dalam kod sumber mesti ditandai sebagai sintetik, kecuali untuk konstruktor lalai dan kaedah inisialisasi kelas." Petunjuk lebih lanjut mengenai makna sintetik di Java dapat dilihat dalam dokumentasi Javadoc untuk Member.isSynthetic (). Dokumentasi kaedah itu menyatakan bahawa ia mengembalikan "benar jika dan hanya jika anggota ini diperkenalkan oleh penyusun." Saya suka definisi "sintetik" yang sangat pendek: konstruksi Java yang diperkenalkan oleh penyusun.

Penyusun Java mesti membuat kaedah sintetik pada kelas bersarang apabila atributnya yang ditentukan dengan pengubah peribadi diakses oleh kelas lampiran. Contoh kod seterusnya menunjukkan keadaan ini.

DemonstrateSyntheticMethods.java (Melampirkan Kelas Memohon Atribut Peribadi Satu Kelas Bersarang)

package dustin.examples; import java.util.Calendar; import static java.lang.System.out; public final class DemonstrateSyntheticMethods { public static void main(final String[] arguments) { DemonstrateSyntheticMethods.NestedClass nested = new DemonstrateSyntheticMethods.NestedClass(); out.println("String: " + nested.highlyConfidential); } private static final class NestedClass { private String highlyConfidential = "Don't tell anyone about me"; private int highlyConfidentialInt = 42; private Calendar highlyConfidentialCalendar = Calendar.getInstance(); private boolean highlyConfidentialBoolean = true; } } 

Kod di atas menyusun tanpa kejadian. Apabila javap dijalankan terhadap .classfile yang disusun , outputnya seperti yang ditunjukkan dalam snapshot skrin berikut.

Seperti yang ditunjukkan oleh snapshot skrin di atas, kaedah sintetik dengan nama access$100telah dibuat di kelas bersarang NestedClassuntuk memberikan String peribadinya ke kelas lampiran. Perhatikan bahawa kaedah sintetik hanya ditambahkan untuk atribut peribadi tunggal dari NestedClass yang diakses oleh kelas lampiran. Sekiranya saya menukar kelas lampiran untuk mengakses semua atribut peribadi NestedClass, kaedah sintetik tambahan akan dihasilkan. Contoh kod seterusnya menunjukkan melakukan ini dan snapshot skrin mengikutnya membuktikan bahawa empat kaedah sintetik dihasilkan dalam kes tersebut.

DemonstrateSyntheticMethods.java (Melampirkan Kelas Memohon Atribut Persendirian Empat Kelas Bersarang)

package dustin.examples; import java.util.Calendar; import static java.lang.System.out; public final class DemonstrateSyntheticMethods { public static void main(final String[] arguments) { DemonstrateSyntheticMethods.NestedClass nested = new DemonstrateSyntheticMethods.NestedClass(); out.println("String: " + nested.highlyConfidential); out.println("Int: " + nested.highlyConfidentialInt); out.println("Calendar: " + nested.highlyConfidentialCalendar); out.println("Boolean: " + nested.highlyConfidentialBoolean); } private static final class NestedClass { private String highlyConfidential = "Don't tell anyone about me"; private int highlyConfidentialInt = 42; private Calendar highlyConfidentialCalendar = Calendar.getInstance(); private boolean highlyConfidentialBoolean = true; } } 

Seperti dua coretan kod sebelumnya di atas dan gambar yang berkaitan ditunjukkan, penyusun Java memperkenalkan kaedah sintetik berdasarkan keperluan. Apabila hanya salah satu atribut peribadi kelas bersarang yang diakses oleh kelas lampiran, hanya satu kaedah sintetik ( access$100) yang dibuat oleh penyusun. Walau bagaimanapun, apabila keempat-empat sifat-sifat peribadi kelas bersarang telah diakses oleh kelas yang disertakan, empat kaedah sintetik sepadan dijana oleh pengkompil ( access$100, access$200, access$300, dan access$400).

Dalam semua kes kelas melampirkan yang mengakses data peribadi kelas bersarangnya, kaedah sintetik dibuat untuk membolehkan akses itu berlaku. Apa yang berlaku apabila kelas bersarang menyediakan aksesor untuk data peribadinya yang boleh digunakan oleh kelas lampiran? Itu ditunjukkan dalam penyenaraian kod seterusnya dan dalam outputnya seperti yang ditunjukkan dalam snapshot skrin seterusnya.

DemonstrateSyntheticMethods.java dengan Accessor Public Class Nested untuk Data Peribadi

package dustin.examples; import java.util.Calendar; import java.util.Date; import static java.lang.System.out; public final class DemonstrateSyntheticMethods { public static void main(final String[] arguments) { DemonstrateSyntheticMethods.NestedClass nested = new DemonstrateSyntheticMethods.NestedClass(); out.println("String: " + nested.highlyConfidential); out.println("Int: " + nested.highlyConfidentialInt); out.println("Calendar: " + nested.highlyConfidentialCalendar); out.println("Boolean: " + nested.highlyConfidentialBoolean); out.println("Date: " + nested.getDate()); } private static final class NestedClass { private String highlyConfidential = "Don't tell anyone about me"; private int highlyConfidentialInt = 42; private Calendar highlyConfidentialCalendar = Calendar.getInstance(); private boolean highlyConfidentialBoolean = true; private Date date = new Date(); public Date getDate() { return this.date; } } } 

Cuplikan skrin di atas menunjukkan bahawa penyusun tidak perlu menghasilkan kaedah sintetik untuk mengakses atribut Tarikh peribadi di kelas bersarang kerana kelas penyekat mengakses atribut tersebut melalui getDate()kaedah yang disediakan . Walaupun dengan getDate()syarat, penyusun akan menghasilkan kaedah sintetik untuk mengakses datekod lampiran telah ditulis untuk mengakses dateatribut secara langsung (sebagai harta benda) dan bukan melalui kaedah aksesor.

Petikan skrin terakhir membawa pemerhatian yang lain. Seperti kaedah yang baru ditambahkan getDate()ditunjukkan dalam snapshot skrin, pengubah seperti publicdimasukkan dalam output javap. Kerana tidak ada pengubah yang ditunjukkan untuk kaedah sintetik yang dibuat oleh penyusun, kita tahu bahawa mereka adalah tahap pakej (atau paket-peribadi). Ringkasnya, penyusun telah membuat kaedah pakej-peribadi untuk mengakses atribut peribadi.

API refleksi Java menyediakan pendekatan lain untuk menentukan kaedah sintetik. Penyenaraian kod seterusnya adalah untuk skrip Groovy yang akan menggunakan Java refleksi API untuk memberikan perincian mengenai kaedah kelas bersarang yang ditunjukkan di atas dengan mudah.

reflectOnMethods.groovy

#!/usr/bin/env groovy import java.lang.reflect.Method import java.lang.reflect.Modifier if (args == null || args.size() < 2) { println "Outer and nested class names must be provided." println "\nUsage #1: reflectOnMethods qualifiedOuterClassName nestedClassName\n" println "\nUsage #2: groovy -cp classpath reflectOnMethods.groovy qualifiedOuterClassName nestedClassName\n" println "\t1. Include outer and nested classes on classpath if necessary" println "\t2. Do NOT include \$ on front of nested class name.\n" System.exit(-1) } def enclosingClassName = args[0] def nestedClassName = args[1] def fullNestedClassName = enclosingClassName + '$' + nestedClassName def enclosingClass = Class.forName(enclosingClassName) Class nestedClass = null enclosingClass.declaredClasses.each { if (!nestedClass && fullNestedClassName.equals(it.name)) { nestedClass = it } } if (nestedClass == null) { println "Unable to find nested class ${fullNestedClassName}" System.exit(-2) } // Use declaredMethods because don't care about inherited methods nestedClass.declaredMethods.each { print "\nMethod '${it.name}' " print "is ${getScopeModifier(it)} scope, " print "${it.synthetic ? 'is synthetic' : 'is NOT synthetic'}, and " println "${it.bridge ? 'is bridge' : 'is NOT bridge'}." } def String getScopeModifier(Method method) { def modifiers = method.modifiers def isPrivate = Modifier.isPrivate(modifiers) def isPublic = Modifier.isPublic(modifiers) def isProtected = Modifier.isProtected(modifiers) String scopeString = "package-private" // default if (isPublic) { scopeString = "public" } else if (isProtected) { scopeString = "protected" } else if (isPrivate) { scopeString = "private" } return scopeString } 

Apabila skrip Groovy di atas dijalankan terhadap kelas dan kelas bersarang yang ditunjukkan di atas, outputnya adalah seperti yang ditunjukkan dalam snapshot skrin seterusnya.

Hasil skrip Groovy yang ditunjukkan dalam gambar sebelumnya mengesahkan apa yang telah diberitahu oleh javap kepada kami: terdapat empat kaedah sintetik dan satu kaedah bukan sintetik yang ditentukan pada kelas bersarang NestedClass. Skrip juga memberitahu kita bahawa kaedah sintetik yang dihasilkan penyusun adalah skop paket-peribadi

Penambahan kaedah sintetik ke kelas bersarang pada tahap skop paket-swasta bukanlah satu-satunya perkara yang dilakukan penyusun dalam contoh di atas. Ia juga mengubah ruang lingkup kelas bersarang itu sendiri dari tetapan peribadi dalam kod menjadi pakej-peribadi dalam .classfail. Memang, sementara kaedah sintetik hanya ditambahkan dalam keadaan di mana kelas lampiran mengakses atribut peribadi, penyusun selalu menjadikan pakej kelas bersarang-peribadi walaupun dinyatakan sebagai peribadi dalam kod. Berita baiknya adalah bahawa ini adalah artifak yang dihasilkan dari proses penyusunan, yang bermaksud bahawa kod tidak dapat disusun sebagaimana bertentangan dengan tahap skop yang diubah dari kelas bersarang atau kaedah sintetiknya. Runtime adalah di mana perkara boleh menjadi tidak jelas.

Kelas tersebut, Rogue, cuba mengakses beberapa kaedah sintetik NestedClass. Kod sumbernya ditunjukkan seterusnya, diikuti oleh kesalahan pengkompil yang dilihat semasa cuba menyusun kod sumber Rogue ini.

Rogue.java cuba mengakses kaedah sintetik pada masa penyusunan

package dustin.examples; import static java.lang.System.out; public class Rogue { public static void main(final String[] arguments) { out.println(DemonstrateSyntheticMethods.NestedClass.getDate()); } } 

Kod di atas tidak akan menyusun, walaupun untuk kaedah bukan sintetik getDate(), dan melaporkan ralat ini:

Buildfile: C:\java\examples\synthetic\build.xml -init: compile: [javac] Compiling 1 source file to C:\java\examples\synthetic\classes [javac] C:\java\examples\synthetic\src\dustin\examples\Rogue.java:9: dustin.examples.DemonstrateSyntheticMethods.NestedClass has private access in dustin.examples.DemonstrateSyntheticMethods [javac] out.println(DemonstrateSyntheticMethods.NestedClass.getDate()); [javac] ^ [javac] 1 error BUILD FAILED C:\java\examples\synthetic\build.xml:29: Compile failed; see the compiler error output for details. Total time: 1 second 

Seperti yang ditunjukkan oleh mesej ralat kompilasi di atas, kaedah tidak sintetik pada kelas bersarang tidak dapat diakses pada waktu penyusunan kerana kelas bersarang mempunyai ruang lingkup peribadi. Dalam artikelnya Java Insecurity: Accounting for Subtleties That Can Compromise Code, Charlie Lai membincangkan potensi situasi di mana perubahan yang diperkenalkan oleh penyusun ini adalah kelemahan keselamatan. Faisal Feroz melangkah lebih jauh dan menyatakan, dalam posting Cara Menulis Kod Java Aman, "Jangan gunakan Kelas Dalam" (lihat Kelas Bertingkat, Batin, Anggota, dan Tingkat Atas untuk perincian mengenai kelas dalaman sebagai subset kelas bersarang) .

Banyak dari kita dapat pergi dalam pengembangan Java untuk waktu yang lama tanpa memerlukan pemahaman yang signifikan mengenai kaedah sintetik. Namun, ada situasi di mana kesedaran mengenai perkara ini penting. Selain masalah keselamatan yang berkaitan dengan ini, juga harus mengetahui apa yang mereka ada ketika membaca jejak timbunan. Kaedah nama-nama seperti access$100, access$200, access$300, access$400, access$500, access$600, dan access$1000dalam jejak tindanan menggambarkan kaedah sintetik yang dihasilkan oleh pengkompil.

Pos Asal Terdapat di //marxsoftware.blogspot.com/

.

Kisah ini, "Kaedah Sintetik Java" pada awalnya diterbitkan oleh JavaWorld.