Programování

Syntetické metody Java

V tomto příspěvku na blogu se podívám na koncept syntetických metod Java. Příspěvek shrnuje, co je syntetická metoda Java, jak ji lze vytvořit a identifikovat, a dopad syntetických metod Java na vývoj Java.

Specifikace jazyka Java (část 13.1) uvádí „Všechny konstrukty zavedené kompilátorem, které ve zdrojovém kódu nemají odpovídající konstrukt, musí být označeny jako syntetické, s výjimkou výchozích konstruktorů a metody inicializace třídy.“ Další vodítka ohledně významu syntetického v Javě najdete v dokumentaci Javadoc pro Member.isSynthetic (). Dokumentace této metody uvádí, že vrací „true právě tehdy, pokud byl tento člen zaveden kompilátorem.“ Líbí se mi ta velmi krátká definice „syntetického“: konstrukt Java zavedený překladačem.

Kompilátor Java musí vytvářet syntetické metody na vnořených třídách, když k jejich atributům zadaným pomocí soukromého modifikátoru přistupuje uzavírající třída. Následující ukázka kódu označuje tuto situaci.

DemonstrateSyntheticMethods.java (obklopující třída vyvolá soukromý atribut vnořené třídy)

příklady zásilky; import java.util.Calendar; importovat 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); } soukromá statická závěrečná třída NestedClass {private String vysoceConfidential = "Nikomu o mně neříkej"; private int vysoceConfidentialInt = 42; soukromý kalendář vysoceConfidentialCalendar = Calendar.getInstance (); private boolean highlyConfidentialBoolean = true; }} 

Výše uvedený kód se kompiluje bez incidentů. Když je spuštěn javap proti kompilovanému .třída souboru, výstup je uveden na následujícím snímku obrazovky.

Jak naznačuje výše uvedený snímek obrazovky, syntetická metoda s názvem přístup 100 $ byl vytvořen ve vnořené třídě Vnořená třída poskytnout svůj soukromý řetězec uzavírající třídě. Všimněte si, že syntetická metoda je přidána pouze pro jediný soukromý atribut NestedClass, ke kterému přistupující třída přistupuje. Pokud změním uzavírací třídu pro přístup ke všem soukromým atributům NestedClass, budou vygenerovány další syntetické metody. Následující příklad kódu ukazuje, že právě děláte, a snímek obrazovky, který následuje, dokazuje, že se v takovém případě vygenerují čtyři syntetické metody.

DemonstrateSyntheticMethods.java (obklopující třída vyvolá čtyři vnořené soukromé atributy třídy)

příklady zásilky; import java.util.Calendar; importovat 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); } soukromá statická závěrečná třída NestedClass {private String vysoceConfidential = "Nikomu o mně neříkej"; private int vysoceConfidentialInt = 42; soukromý kalendář vysoceConfidentialCalendar = Calendar.getInstance (); private boolean highlyConfidentialBoolean = true; }} 

Jak ukazují předchozí dva fragmenty kódu výše a související obrázky, kompilátor Java zavádí syntetické metody podle potřeby. Když byla uzavřená třída přístupná pouze k jednomu ze soukromých atributů vnořené třídy, byla použita pouze jedna syntetická metoda (přístup 100 $) byl vytvořen překladačem. Když však byly všechny čtyři soukromé atributy vnořené třídy zpřístupněny uzavírající třídou, kompilátor vygeneroval čtyři odpovídající syntetické metody (přístup 100 $, přístup 200 $, přístup 300 $, a přístup 400 $).

Ve všech případech uzavírající třídy, která přistupuje k soukromým datům své vnořené třídy, byla vytvořena syntetická metoda, která umožňuje tento přístup. Co se stane, když vnořená třída poskytuje přístupový objekt pro svá soukromá data, která může použít obklopující třída? To je ukázáno v dalším výpisu kódu a na jeho výstupu, jak je znázorněno na následujícím snímku obrazovky.

DemonstrateSyntheticMethods.java s Nested Class Public Accessor pro soukromá data

příklady zásilky; import java.util.Calendar; import java.util.Date; importovat 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 ("Datum:" + nested.getDate ()); } soukromá statická závěrečná třída NestedClass {private String vysoceConfidential = "Nikomu o mně neříkej"; private int vysoceConfidentialInt = 42; soukromý kalendář vysoceConfidentialCalendar = Calendar.getInstance (); private boolean highlyConfidentialBoolean = true; soukromé Datum datum = nové Datum (); public Date getDate () {return this.date; }}} 

Výše uvedený snímek obrazovky ukazuje, že kompilátor nepotřeboval generovat syntetickou metodu pro přístup k atributu private Date ve vnořené třídě, protože obklopující třída k tomuto atributu přistupovala prostřednictvím zadaného getDate () metoda. I s getDate () za předpokladu, že by překladač vygeneroval syntetickou metodu pro přístup k datum má přiložený kód napsán pro přístup k datum atribut přímo (jako vlastnost), nikoli prostřednictvím metody přístupového objektu.

Poslední snímek obrazovky přináší další pozorování. Jak nově přidáno getDate () metoda zobrazuje v tomto snímku obrazovky modifikátory jako veřejnost jsou součástí výstupu javapu. Protože se pro syntetické metody vytvořené kompilátorem nezobrazuje žádný modifikátor, víme, že jsou na úrovni balíčku (nebo soukromého balíčku). Stručně řečeno, kompilátor vytvořil soukromé metody balíku pro přístup k soukromým atributům.

Rozhraní API pro reflexi Java poskytují další přístup k určování syntetických metod. Další výpis kódu je pro skript Groovy, který bude používat rozhraní Java Reflection API k pohodlnému poskytování podrobností o metodách vnořené třídy uvedených výše.

reflectOnMethods.groovy

#! / usr / bin / env groovy import java.lang.reflect.Method import java.lang.reflect.Modifier if (args == null || args.size () <2) {println "Vnější a vnořené názvy tříd musí být být poskytnuty." println "\ nUsage # 1: reflectOnMethods qualifiedOuterClassName nestedClassName \ n" println "\ nUsage # 2: groovy -cp classpath reflectOnMethods.groovy kvalifikovanéOuterClassName vnořenéClassName \ n" println "\ t1. V případě potřeby zahrnout vnější a vnořené třídy" print " t2. NEZAHRNUJTE \ $ před název vnořené třídy. \ n "System.exit (-1)} def enclosingClassName = args [0] def nestedClassName = args [1] def fullNestedClassName = enclosingClassName + '$' + nestedClassName def enclosingClass = Class.forName (enclosingClassName) Třída nestedClass = null enclosingClass.declaredClasses.each {if (! nestedClass && fullNestedClassName.equals (it.name)) {nestedClass = it}} if (nestedClass == null) {println "Nelze najít vnořenou třídu $ {fullNestedClassName} "System.exit (-2)} // Použít deklarované metody, protože se nestarat o zděděné metody nestedClass.declaredMethods.each {tisk" \ nMetoda '$ {it.name}' "tisk" je $ {getScopeModifier (it)} rozsah, "tisk" $ {it.synthetic? 'je syntetický': „NENÍ 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" // výchozí if (isPublic) {scopeString = "public"} else if (isProtected) {scopeString = "protected"} else if (isPrivate) {scopeString = "private" } vrátit rozsahString} 

Když je výše uvedený Groovy skript spuštěn proti třídě a vnořené třídě zobrazené výše, výstup je ten, který je uveden na dalším snímku obrazovky.

Výsledky skriptu Groovy zobrazené na předchozím obrázku ověřují to, co nám již řekl javap: ve vnořené třídě jsou definovány čtyři syntetické metody a jedna nesyntetická metoda Vnořená třída. Skript nám také říká, že syntetické metody generované překladačem jsou oborem soukromého balíku.

Přidání syntetických metod do vnořené třídy na úrovni soukromého balíčku není jedinou věcí, kterou kompilátor provedl ve výše uvedeném příkladu. Také změnil rozsah samotné vnořené třídy ze soukromého nastavení v kódu na balíček-soukromý v .třída soubor. Ve skutečnosti, zatímco syntetické metody byly přidány pouze v případě, kdy uzavírající třída přistupovala k soukromému atributu, kompilátor vždy vytvoří balíček vnořené třídy soukromý, i když je v kódu zadán jako soukromý. Dobrou zprávou je, že se jedná o výsledný artefakt procesu kompilace, což znamená, že kód nelze kompilovat tak, jak je, proti změněné úrovni rozsahu vnořené třídy nebo jejích syntetických metod. Runtime je místo, kde se věci mohou dostat do kostek.

Třída Rogue se pokouší získat přístup k některým syntetickým metodám NestedClass. Dále se zobrazí jeho zdrojový kód, za ním následuje chyba kompilátoru, která se zobrazí při pokusu o kompilaci tohoto zdrojového kódu Rogue.

Rogue.java se pokouší získat přístup k syntetickým metodám v době kompilace

příklady zásilky; importovat statický java.lang.System.out; public class Rogue {public static void main (final String [] argumenty) {out.println (DemonstrateSyntheticMethods.NestedClass.getDate ()); }} 

Výše uvedený kód nebude kompilován, ani pro nesyntetickou metodu getDate ()a hlásí tuto chybu:

Buildfile: C: \ java \ examples \ syntetický \ build.xml -init: kompilace: [javac] Kompilace 1 zdrojového souboru do C: \ java \ examples \ syntetické \ třídy [javac] C: \ java \ examples \ syntetický \ src \ dustin \ examples \ Rogue.java: 9: dustin.examples.DemonstrateSyntheticMethods.NestedClass má soukromý přístup v dustin.examples.DemonstrateSyntheticMethods [javac] out.println (DemonstrateSyntheticMethods.NestedClass.getDate ()); [javac] ^ [javac] 1 chyba BUILD FAILED C: \ java \ examples \ syntetický \ build.xml: 29: Kompilace se nezdařila; podrobnosti najdete v chybovém výstupu kompilátoru. Celkový čas: 1 sekunda 

Jak naznačuje výše uvedená chybová zpráva kompilace, i nesyntetická metoda ve vnořené třídě je nepřístupná v době kompilace protože vnořená třída má soukromý rozsah. Charlie Lai ve svém článku Java Insecurities: Accounting for Subtleties that Can Compromise Code popisuje potenciální situace, ve kterých jsou těmito změnami zavedenými kompilátorem chyby zabezpečení. Faisal Feroz jde dále a v příspěvku Jak psát zabezpečený kód Java uvádí „Nepoužívejte vnitřní třídy“ (podrobnosti o vnitřních třídách naleznete v části Vnořené, vnitřní, členské a nejvyšší úrovně jako podmnožina vnořených tříd) .

Mnoho z nás může pokračovat ve vývoji Java dlouho, aniž by potřebovalo důkladné porozumění syntetickým metodám. Existují však situace, kdy je povědomí o nich důležité. Kromě bezpečnostních problémů, které s nimi souvisejí, je třeba si také uvědomit, co jsou při čtení trasování zásobníku. Názvy metod, jako je přístup 100 $, přístup 200 $, přístup 300 $, přístup 400 $, přístup 500 $, přístup 600 $, a přístup 1000 $ ve stopě zásobníku odrážejí syntetické metody generované kompilátorem.

Původní příspěvek k dispozici na //marxsoftware.blogspot.com/

.

Tento příběh, „Java's Synthetic Methods“, původně publikoval JavaWorld.