Programovanie

Syntetické metódy Java

V tomto blogovom príspevku sa zaoberám konceptom syntetických metód Java. Príspevok sumarizuje, čo je syntetická metóda Java, ako ju možno vytvoriť a identifikovať a aké sú dôsledky syntetických metód Java na vývoj Java.

Špecifikácia jazyka Java (časť 13.1) uvádza: „Všetky konštrukty zavedené kompilátorom, ktoré nemajú zodpovedajúci konštrukt v zdrojovom kóde, musia byť označené ako syntetické, s výnimkou predvolených konštruktorov a metódy inicializácie triedy.“ Ďalšie indície týkajúce sa významu syntetického v Jave nájdete v dokumentácii Javadoc pre Member.isSynthetic (). V dokumentácii k tejto metóde sa uvádza, že vráti hodnotu „true, len ak tento člen zaviedol kompilátor.“ Páči sa mi veľmi krátka definícia pojmu „syntetický“: konštrukcia Java zavedená kompilátorom.

Kompilátor Java musí vytvárať syntetické metódy na vnorených triedach, keď k ich atribútom zadaným pomocou súkromného modifikátora pristupuje ohraničujúca trieda. Nasledujúca ukážka kódu naznačuje túto situáciu.

DemonstrateSyntheticMethods.java (priložená trieda vyvolá jeden vnorený súkromný atribút triedy)

balenie zásypu.príklady; import java.util.Calendar; importovať statický java.lang.System.out; public final class DemonstrateSyntheticMethods {public static void main (final String [] argumenty) {DemonstrateSyntheticMethods.NestedClass nested = new DemonstrateSyntheticMethods.NestedClass (); out.println ("String:" + nested.highlyConfidential); } súkromná statická záverečná trieda NestedClass {private String highlyConfidential = "Nehovorte o mne nikomu"; private int vysoceConfidentialInt = 42; súkromný kalendár highlyConfidentialCalendar = Calendar.getInstance (); private boolean highlyConfidentialBoolean = true; }} 

Vyššie uvedený kód sa zostavuje bez problémov. Keď je javap spustený proti skompilovanému .trieda súboru, výstup je uvedený na nasledujúcej snímke obrazovky.

Ako naznačuje vyššie uvedená snímka obrazovky, syntetická metóda s názvom prístup 100 dolárov bol vytvorený na vnorenej triede NestedClass aby poskytla svoj súkromný reťazec priloženej triede. Upozorňujeme, že syntetická metóda sa pridáva iba pre jediný súkromný atribút NestedClass, ku ktorému pristupuje priložená trieda. Ak zmením uzatváraciu triedu na prístup ku všetkým súkromným atribútom NestedClass, vygenerujú sa ďalšie syntetické metódy. Nasledujúci príklad kódu demonštruje práve toto a nasledujúca snímka obrazovky dokazuje, že v takom prípade sú vygenerované štyri syntetické metódy.

DemonstrateSyntheticMethods.java (priložená trieda vyvoláva štyri súkromné ​​atribúty vnorenej triedy)

balenie zásypu.príklady; import java.util.Calendar; importovať statický java.lang.System.out; public final class DemonstrateSyntheticMethods {public static void main (final String [] argumenty) {DemonstrateSyntheticMethods.NestedClass nested = new DemonstrateSyntheticMethods.NestedClass (); out.println ("String:" + nested.highlyConfidential); out.println ("Int:" + nested.highlyConfidentialInt); out.println ("Kalendár:" + nested.highlyConfidentialCalendar); out.println ("Boolean:" + nested.highlyConfidentialBoolean); } súkromná statická záverečná trieda NestedClass {private String highlyConfidential = "Nehovorte o mne nikomu"; private int vysoceConfidentialInt = 42; súkromný kalendár highlyConfidentialCalendar = Calendar.getInstance (); private boolean highlyConfidentialBoolean = true; }} 

Ako ukazujú predchádzajúce dva útržky kódu vyššie a súvisiace obrázky, kompilátor Java zavádza syntetické metódy podľa potreby. Keď bola uzatváracou triedou prístupná iba jeden zo súkromných atribútov vnorenej triedy, bola použitá iba jedna syntetická metóda (prístup 100 dolárov) vytvoril kompilátor. Keď však prístupová trieda sprístupnila všetky štyri súkromné ​​atribúty vnorenej triedy, kompilátor vygeneroval štyri zodpovedajúce syntetické metódy (prístup 100 dolárov, prístup 200 dolárov, prístup 300 dolárova prístup 400 dolárov).

Vo všetkých prípadoch prístupovej triedy, ktorá pristupovala k súkromným údajom svojej vnorenej triedy, bola vytvorená syntetická metóda, ktorá umožňuje tento prístup. Čo sa stane, keď vnorená trieda poskytne prístupový objekt pre svoje súkromné ​​údaje, ktoré môže obklopujúca trieda použiť? To je demonštrované v nasledujúcom zozname kódov a na jeho výstupe, ako je to znázornené na nasledujúcej snímke obrazovky.

DemonstrateSyntheticMethods.java s verejným prístupovým mechanizmom vnorenej triedy pre súkromné ​​údaje

balenie zásypu.príklady; import java.util.Calendar; import java.util.Date; importovať statický java.lang.System.out; public final class DemonstrateSyntheticMethods {public static void main (final String [] argumenty) {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 ("Dátum:" + nested.getDate ()); } súkromná statická záverečná trieda NestedClass {private String highlyConfidential = "Nehovorte o mne nikomu"; private int vysoceConfidentialInt = 42; súkromný kalendár highlyConfidentialCalendar = Calendar.getInstance (); private boolean highlyConfidentialBoolean = true; súkromné ​​Dátum dátum = nový Dátum (); public Date getDate () {return this.date; }}} 

Vyššie uvedená snímka obrazovky demonštruje, že kompilátor nemusel generovať syntetickú metódu prístupu k atribútu private Date vo vnorenej triede, pretože uzatváracia trieda k tomuto atribútu pristupovala prostredníctvom poskytnutej getDate () metóda. Aj s getDate () za predpokladu, že by kompilátor vygeneroval syntetickú metódu pre prístup k dátum má napísaný priložený kód pre prístup k dátum atribút priamo (ako vlastnosť) a nie prostredníctvom prístupovej metódy.

Posledná snímka obrazovky prináša ďalšie pozorovanie. Ako novo pridané getDate () metóda zobrazuje na danom snímku obrazovky modifikátory ako verejné sú zahrnuté vo výstupe javapu. Pretože sa pre syntetické metódy vytvorené kompilátorom nezobrazuje žiadny modifikátor, vieme, že sú na úrovni balíka (alebo balíku súkromné). Stručne povedané, kompilátor vytvoril súkromné ​​metódy balíka pre prístup k súkromným atribútom.

Rozhranie API Java reflect poskytuje ďalší prístup k určovaniu syntetických metód. Nasledujúci zoznam kódov je pre skript Groovy, ktorý bude používať rozhrania API pre reflexiu Java na pohodlné poskytnutie podrobností o vyššie uvedených metódach vnorenej triedy.

reflectOnMethods.groovy

#! / usr / bin / env groovy import java.lang.reflect.Method import java.lang.reflect.Modifier if (args == null || args.size () <2) {println "Vonkajšie a vnorené názvy tried musia byť byť poskytovaný." println "\ nUsage # 1: reflectOnMethods qualifiedOuterClassName nestedClassName \ n" println "\ nUsage # 2: groovy -cp classpath reflectOnMethods.groovy qualifiedOuterClassName nestedClassName \ n" println "\ t1. V prípade potreby zahrnúť vonkajšie a vnorené triedy do triedy tlače" t2. Nezahŕňa \ $ pred názov vnorenej triedy. \ n "System.exit (-1)} def enclosingClassName = args [0] def nestedClassName = args [1] def fullNestedClassName = enclosingClassName + '$' + nestedClassName def enclosingClass = Class.forName (enclosingClassName) Trieda nestedClass = null enclosingClass.declaredClasses.each {if (! nestedClass && fullNestedClassName.equals (it.name)) {nestedClass = it}} if (nestedClass == null) {println "Nedá sa nájsť vnorenú triedu $ {fullNestedClassName} "System.exit (-2)} // Použiť deklarované metódy, pretože sa nestaráte o zdedené metódy nestedClass.declaredMethods.each {print" \ nMetóda '$ {it.name}' "print" je $ {getScopeModifier (it)} rozsah, "tlač" $ {it.synthetic? 'je syntetický': „NIE je syntetický“} a „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 (modifikátory) String scopeString = "package-private" // predvolené if (isPublic) {scopeString = "public"} else if (isProtected) {scopeString = "protected"} else if (isPrivate) {scopeString = "private" } return scopeString} 

Keď sa vyššie uvedený skript Groovy vykoná proti triede a vnorenej triede zobrazenej vyššie, výstup bude taký, aký je uvedený na nasledujúcej snímke obrazovky.

Výsledky skriptu Groovy zobrazeného na predchádzajúcom obrázku overujú to, čo nám už povedal javap: vo vnorenej triede sú definované štyri syntetické metódy a jedna nesyntetická metóda NestedClass. Skript nám tiež hovorí, že syntetické metódy generované kompilátorom sú súkromným balíkom.

Pridanie syntetických metód do vnorenej triedy na úrovni balíka-súkromného rozsahu nie je jediné, čo kompilátor urobil vo vyššie uvedenom príklade. Tiež sa zmenil rozsah samotnej vnorenej triedy zo súkromného nastavenia v kóde na balík-súkromný v .trieda spis. Zatiaľ čo syntetické metódy boli skutočne pridané iba v prípade, keď uzatváracia trieda získala prístup k súkromnému atribútu, kompilátor vždy urobí zo vnorenej triedy balík súkromný, aj keď je v kóde uvedený ako súkromný. Dobrou správou je, že ide o výsledný artefakt procesu kompilácie, čo znamená, že kód nie je možné kompilovať tak, ako je, proti zmenenej úrovni rozsahu vnorenej triedy alebo jej syntetickým metódam. Runtime je miesto, kde sa veci môžu stať riskantnými.

Trieda, Rogue, sa pokúša získať prístup k niektorým zo syntetických metód NestedClass. Ďalej sa zobrazí jeho zdrojový kód, za ktorým nasleduje chyba kompilátora, ktorá sa vyskytla pri pokuse o kompiláciu tohto zdrojového kódu Rogue.

Rogue.java sa pokúša získať prístup k syntetickým metódam v čase kompilácie

balenie zásypu.príklady; importovať statický java.lang.System.out; public class Rogue {public static void main (final String [] argumenty) {out.println (DemonstrateSyntheticMethods.NestedClass.getDate ()); }} 

Vyššie uvedený kód sa nebude kompilovať, a to ani pri nesyntetickej metóde getDate ()a nahlási túto chybu:

Buildfile: C: \ java \ examples \ syntetický \ build.xml -init: kompilácia: [javac] Kompilácia 1 zdrojového súboru do C: \ java \ examples \ syntetické \ triedy [javac] C: \ java \ examples \ syntetický \ src \ dustin \ examples \ Rogue.java: 9: dustin.examples.DemonstrateSyntheticMethods.NestedClass má súkromný prístup v dustin.examples.DemonstrateSyntheticMethods [javac] out.println (DemonstrateSyntheticMethods.NestedClass.getDate ()); [javac] ^ [javac] 1 chyba BUILD FAILED C: \ java \ examples \ syntetický \ build.xml: 29: kompilácia zlyhala; podrobnosti nájdete na výstupe chyby kompilátora. Celkový čas: 1 sekunda 

Ako naznačuje vyššie uvedená chybová správa kompilácie, dokonca aj nesyntetická metóda na vnorenej triede je neprístupná v čase zostavenia pretože vnorená trieda má súkromný rozsah. Charlie Lai vo svojom článku Java Insecurities: Accounting for Subtleties that Can Compromise Code pojednáva o možných situáciách, v ktorých sú týmito zmenami zavedenými kompilátorom bezpečnostné chyby. Faisal Feroz ide ďalej a v príspevku Ako napísať bezpečný kód Java uvádza „Nepoužívajte vnútorné triedy“ (podrobnosti o vnútorných triedach nájdete v časti Vnorené, Vnútorné triedy, Členské triedy a triedy najvyššej úrovne ako podmnožina vnorených tried) .

Mnoho z nás môže pokračovať vo vývoji Java dlho bez toho, aby potrebovali dôkladné pochopenie syntetických metód. Existujú však situácie, kedy je ich povedomie dôležité. Okrem bezpečnostných problémov, ktoré s nimi súvisia, je tiež potrebné pamätať na to, čo sú, keď čítate stopy zásobníka. Názvy metód ako napr prístup 100 dolárov, prístup 200 dolárov, prístup 300 dolárov, prístup 400 dolárov, prístup 500 dolárov, prístup 600 dolárova prístup 1 000 dolárov v sledovaní zásobníka odrážajú syntetické metódy generované kompilátorom.

Pôvodný príspevok je k dispozícii na //marxsoftware.blogspot.com/

.

Tento príbeh, „Syntetické metódy Java“, pôvodne publikoval server JavaWorld.

$config[zx-auto] not found$config[zx-overlay] not found