Javine sintetske metode

U ovom blogu gledam koncept Java sintetičkih metoda. Post sažima što je sintetička metoda Java, kako se može stvoriti i identificirati te implikacije sintetičkih metoda Java na razvoj Jave.

Specifikacija jezika Java (odjeljak 13.1) kaže "Bilo koji konstrukt koji je uveo kompajler i koji nema odgovarajući konstrukt u izvornom kodu mora biti označen kao sintetički, osim zadanih konstruktora i metode inicijalizacije klase." Daljnji tragovi o značenju sintetičkog u Javi mogu se naći u dokumentaciji Javadoc za Member.isSynthetic (). U dokumentaciji te metode stoji da ona vraća "true ako i samo ako je ovog člana uveo kompajler." Sviđa mi se ona vrlo kratka definicija "sintetike": Java konstrukcija koju je uveo kompajler.

Java prevodilac mora stvoriti sintetičke metode na ugniježđenim klasama kada njihovim atributima navedenim s privatnim modifikatorom pristupa klasa koja obuhvaća. Sljedeći uzorak koda ukazuje na ovu situaciju.

DemonstrateSyntheticMethods.java (Prilaganje klase poziva jedan ugniježđeni privatni atribut klase)

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; } } 

Gornji kod kompajlira se bez nezgoda. Kada se javap pokrene protiv prevedene .classdatoteke, izlaz je kao što je prikazano na sljedećoj snimci zaslona.

Kao što gornja snimka zaslona ukazuje, sintetička metoda s imenom access$100stvorena je na ugniježđenoj klasi NestedClasskako bi osigurala svoj privatni niz priloženoj klasi. Imajte na umu da je sintetička metoda dodana samo za jedan privatni atribut NestedClass kojem pristupa klasa koja obuhvaća. Ako promijenim klasu zatvaranja kako bih pristupio svim privatnim atributima NestedClass, generirat će se dodatne sintetske metode. Sljedeći primjer koda pokazuje upravo to, a snimka zaslona nakon njega dokazuje da su u tom slučaju generirane četiri sintetske metode.

DemonstrateSyntheticMethods.java (Prilaganje klase poziva se na četiri ugniježđena privatna atributa klase)

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; } } 

Kao što pokazuju prethodna dva isječka koda i pridružene slike, Java kompajler uvodi sintetske metode prema potrebi. Kada je klasa koja zatvara pristupila samo jednom od privatnih atributa ugniježđene klase, kompajlator access$100je stvorio samo jednu sintetičku metodu ( ). Međutim, kada su sve četiri privatni atributi uklopljenom klase su pristupiti pomoću priključnog klase četiri odgovarajuće metode sinteze je generiran prevodilac ( access$100, access$200, access$300, i access$400).

U svim slučajevima pristupne klase koja pristupa privatnim podacima ugniježđene klase, stvorena je sintetička metoda koja je omogućila da se taj pristup dogodi. Što se događa kada ugniježđena klasa pruža pristup svojim privatnim podacima koje klasa koja obuhvaća može koristiti? To je prikazano u sljedećem popisu kodova i u njegovom izlazu kao što je prikazano na sljedećem snimku zaslona.

DemonstrateSyntheticMethods.java s javnim pristupnikom ugniježđenih klasa za privatne podatke

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; } } } 

Gornja snimka zaslona pokazuje da kompajler nije trebao generirati sintetičku metodu za pristup privatnom atributu Date u ugniježđenoj klasi jer je klasa koja je pridružila tom atributu pristupila putem pružene getDate()metode. Čak i ako je getDate()ponuđen, kompajler bi generirao sintetičku metodu za pristup dateje li priložen kod napisan za pristup dateatributu izravno (kao svojstvo), a ne pomoću metode pristupa.

Posljednja snimka zaslona iznosi još jedno zapažanje. Kao što novo dodana getDate()metoda pokazuje na toj snimci zaslona, ​​modifikatori kao što publicsu uključeni u javap izlaz. Budući da nije prikazan modifikator sintetičkih metoda koje je stvorio kompajler, znamo da su na razini paketa (ili privatno). Ukratko, kompajler je stvorio paketno-privatne metode za pristup privatnim atributima.

API-ji za odražavanje Java pružaju drugi pristup za određivanje sintetičkih metoda. Sljedeći je popis kodova za Groovy skriptu koja će koristiti API-je Java refleksije da bi prikladno pružila detalje o metodama gore ugniježđene klase.

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 } 

Kada se gornja Groovy skripta izvrši protiv klase i ugniježđene klase prikazane gore, izlaz je onaj prikazan na sljedećoj snimci zaslona.

Rezultati skripte Groovy prikazani na prethodnoj slici potvrđuju ono što nam je javap već rekao: na ugniježđenoj su klasi definirane četiri sintetske metode i jedna nesintetička metoda NestedClass. Skripta nam također govori da su sintetičke metode generirane od strane prevoditelja opseg privatnog paketa.

Dodavanje sintetičkih metoda ugniježđenoj klasi na razini privatno-paketnog opsega nije jedino što je prevodilac učinio u gornjem primjeru. Također je promijenio opseg same ugniježđene klase iz privatne postavke u kodu u paket-private u .classdatoteci. U stvari, dok su sintetičke metode dodane samo u slučaju kada je klasa koja je pristupila pristupila privatnom atributu, kompajler uvijek ugniježđenu klasu čini paket-private, čak i ako je u kodu naveden kao private. Dobra vijest je da je ovo rezultirajući artefakt procesa kompilacije, što znači da se kod ne može kompajlirati takav kakav jest protiv promijenjene razine opsega ugniježđene klase ili njezinih sintetičkih metoda. Runtime je mjesto gdje stvari mogu postati zamršene.

Razred, Rogue, pokušava pristupiti nekim od sintetičkih metoda NestedClass. Sljedeći je prikazan njegov izvorni kod, nakon čega slijedi pogreška kompajlera uočena prilikom pokušaja kompajliranja ovog izvornog koda Rogue.

Rogue.java pokušava pristupiti sintetičkim metodama u vrijeme sastavljanja

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

Gornji kod se neće prevesti, čak i za nesintetičku metodu getDate(), i prijavljuje ovu pogrešku:

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 

Kao što gornja poruka o pogrešci kompilacije ukazuje, čak je i nesintetička metoda ugniježđene klase nedostupna u vrijeme prevođenja jer ugniježđena klasa ima privatni opseg. U svom članku Java Insecurities: Accounting for Subtilities That Can Compromise Code, Charlie Lai raspravlja o potencijalnim situacijama u kojima su ove promjene koje je uveo kompajler sigurnosna ranjivost. Faisal Feroz ide dalje i navodi, u postu Kako napisati sigurni Java kôd, "Ne koristi unutarnje razrede" (za detalje o unutarnjim klasama kao podskup ugniježđenih klasa pogledajte Ugnježđene, unutarnje, članske i najviše razine). .

Mnogi od nas mogu dugo ići u razvoju Jave bez potrebe za značajnim razumijevanjem sintetičkih metoda. Međutim, postoje situacije kada je svijest o njima važna. Osim sigurnosnih problema povezanih s njima, također treba biti svjestan što su oni kada čitaju tragove steka. Metoda imena, kao što su access$100, access$200, access$300, access$400, access$500, access$600, te access$1000u tragovima stog odražavaju sintetičkih postupaka generira prevodilac.

Izvorni post dostupan na //marxsoftware.blogspot.com/

.

Ovu priču, "Javine sintetske metode" izvorno je objavio JavaWorld.