Programmering

Java syntetiske metoder

I dette blogginnlegget ser jeg på konseptet med Java-syntetiske metoder. Innlegget oppsummerer hva en Java-syntetisk metode er, hvordan man kan opprette og identifisere, og implikasjonene av Java-syntetiske metoder på Java-utvikling.

Java språkspesifikasjon (avsnitt 13.1) sier "Alle konstruksjoner introdusert av kompilatoren som ikke har en tilsvarende konstruksjon i kildekoden, må merkes som syntetiske, bortsett fra standardkonstruktører og klassens initialiseringsmetode." Ytterligere ledetråder til betydningen av syntetisk på Java finner du i Javadoc-dokumentasjonen for Member.isSynthetic (). Metodens dokumentasjon sier at den returnerer "sant hvis og bare hvis dette medlemmet ble introdusert av kompilatoren." Jeg liker den veldig korte definisjonen av "syntetisk": en Java-konstruksjon introdusert av kompilatoren.

Java-kompilatoren må opprette syntetiske metoder på nestede klasser når attributtene som er spesifisert med den private modifisereren, får tilgang til klassen. Neste kodeeksempel indikerer denne situasjonen.

DemonstrateSyntheticMethods.java (Enclosing Class Invokes One Nested Class Private Attribute)

pakke dustin. eksempler; importere java.util.Calendar; importere statisk java.lang.System.out; offentlig sluttklasse DemonstrateSyntheticMethods {public static void main (final String [] argumenter) {DemonstrateSyntheticMethods.NestedClass nested = new DemonstrateSyntheticMethods.NestedClass (); out.println ("String:" + nestet.highlyConfidential); } privat statisk sluttklasse NestedClass {private String highlyConfidential = "Ikke fortell noen om meg"; private int highlyConfidentialInt = 42; privat kalender highlyConfidentialCalendar = Calendar.getInstance (); privat boolsk highlyConfidentialBoolean = true; }} 

Ovennevnte kode kompileres uten hendelser. Når javap kjøres mot det kompilerte .klasse fil, er utdataene som vist på følgende skjermbilde.

Som øyeblikksbildet ovenfor viser, er en syntetisk metode med navnet få tilgang til $ 100 har blitt opprettet på den nestede klassen NestedClass å gi sin private streng til den omsluttende klassen. Merk at den syntetiske metoden bare legges til for det private private attributtet til NestedClass som den vedlagte klassen har tilgang til. Hvis jeg endrer den vedlagte klassen for å få tilgang til alle private attributter til NestedClass, genereres flere syntetiske metoder. Det neste kodeeksemplet viser å gjøre nettopp dette, og skjermbildet etter det viser at fire syntetiske metoder genereres i så fall.

DemonstrateSyntheticMethods.java (Enclosing Class påkaller fire nestede klasse private attributter)

pakke dustin. eksempler; importere java.util.Calendar; importere statisk java.lang.System.out; offentlig sluttklasse DemonstrateSyntheticMethods {public static void main (final String [] argumenter) {DemonstrateSyntheticMethods.NestedClass nested = new DemonstrateSyntheticMethods.NestedClass (); out.println ("String:" + nestet.highlyConfidential); out.println ("Int:" + nestet.highlyConfidentialInt); out.println ("Kalender:" + nestet.highlyConfidentialCalendar); out.println ("Boolsk:" + nestet.highlyConfidentialBoolean); } privat statisk sluttklasse NestedClass {private String highlyConfidential = "Ikke fortell noen om meg"; private int highlyConfidentialInt = 42; privat kalender highlyConfidentialCalendar = Calendar.getInstance (); privat boolsk highlyConfidentialBoolean = true; }} 

Som de to foregående kodebitene ovenfor og de tilknyttede bildene viser, introduserer Java-kompilatoren syntetiske metoder etter behov. Når bare en av de nestede klassens private attributter ble åpnet av klassen, var det bare en syntetisk metode (få tilgang til $ 100) ble opprettet av kompilatoren. Når alle fire private attributter til den nestede klassen ble åpnet av klassen, ble imidlertid fire tilsvarende syntetiske metoder generert av kompilatoren (få tilgang til $ 100, få tilgang til $ 200, få tilgang til $ 300, og få tilgang til $ 400).

I alle tilfeller av en omsluttende klasse som får tilgang til den nestede klassens private data, ble det laget en syntetisk metode for å tillate at tilgangen skjedde. Hva skjer når den nestede klassen gir tilgang til sine private data som den vedlagte klassen kan bruke? Dette vises i neste kodeliste og i utdataene som vist i neste skjermbilde.

DemonstrateSyntheticMethods.java med Nested Class Public Accessor for Private Data

pakke dustin. eksempler; importere java.util.Calendar; importere java.util.Date; importere statisk java.lang.System.out; offentlig sluttklasse DemonstrateSyntheticMethods {public static void main (final String [] argumenter) {DemonstrateSyntheticMethods.NestedClass nested = new DemonstrateSyntheticMethods.NestedClass (); out.println ("String:" + nestet.highlyConfidential); out.println ("Int:" + nestet.highlyConfidentialInt); out.println ("Kalender:" + nestet.highlyConfidentialCalendar); out.println ("Boolsk:" + nestet.highlyConfidentialBoolean); out.println ("Dato:" + nestet.getDate ()); } privat statisk sluttklasse NestedClass {private String highlyConfidential = "Ikke fortell noen om meg"; private int highlyConfidentialInt = 42; privat kalender highlyConfidentialCalendar = Calendar.getInstance (); privat boolsk highlyConfidentialBoolean = true; privat Dato dato = ny Dato (); public Date getDate () {return this.date; }}} 

Ovenstående skjermbilde viser at kompilatoren ikke trengte å generere en syntetisk metode for å få tilgang til det private Date-attributtet i den nestede klassen fordi den vedlagte klassen fikk tilgang til attributtet via den angitte getDate () metode. Selv med getDate () forutsatt, ville kompilatoren ha generert en syntetisk metode for å få tilgang til Dato har den vedlagte koden blitt skrevet for å få tilgang til Dato attributt direkte (som en eiendom) i stedet for via tilgangsmetoden.

Det siste skjermbildet viser en ny observasjon. Som den nylig lagt til getDate () metoden viser i skjermbildet, modifikatorer som offentlig er inkludert i javap-utdata. Fordi det ikke vises noen modifikatorer for de syntetiske metodene opprettet av kompilatoren, vet vi at de er pakkenivå (eller pakke-private). Kort sagt, kompilatoren har laget pakke-private metoder for tilgang til private attributter.

Java refleksjon APIer gir en annen tilnærming for å bestemme syntetiske metoder. Den neste kodelisten er for et Groovy-skript som vil bruke Java-refleksjon-APIene for å gi detaljer om metodene til den nestede klassen vist ovenfor.

reflectOnMethods.groovy

#! / usr / bin / env groovy import java.lang.reflect.Method import java.lang.reflect.Modifier if (args == null || args.size () <2) {println "Ytre og nestede klassenavn må bli gitt. " println "\ nBruk # 1: reflectOnMethods kvalifiserteOuterClassName nestedClassName \ n" println "\ nBruk # 2: groovy -cp classpath reflectOnMethods.groovy QualifiedOuterClassName nestedClassName \ n" println "\ t1. Inkluder ytre og nestede klasser på classpath t2. IKKE inkluder \ $ foran nestet klassenavn. \ n "System.exit (-1)} def enclosingClassName = args [0] def nestedClassName = args [1] def fullNestedClassName = enclosingClassName + '$' + nestetClassName def enclosingClass = Class.forName (enclosingClassName) Class nestedClass = null enclosingClass.declaredClasses.each {if (! nestedClass && fullNestedClassName.equals (it.name)) {nestedClass = it}} if (nestedClass == null) {println "Kan ikke finn nestet klasse $ {fullNestedClassName} "System.exit (-2)} // Bruk declaratedMethods fordi du ikke bryr deg om arvede metoder nestedClass.declaredMethods.each {print" \ nMethod '$ {it.name}' "print" er $ {getScopeModifier (it)} omfang, "skriv ut" $ {it.synthetic? 'er syntetisk': 'er IKKE syntetisk'}, og "println" $ {it.bridge? 'er bro': 'er IKKE bro'}. "} def String getScopeModifier (Metodemetode) {def modifiers = method.modifiers def isPrivate = Modifier.isPrivate (modifiers) def isPublic = Modifier.isPublic (modifiers) def isProtected = Modifier .isProtected (modifiers) Streng scopeString = "pakke-privat" // standard hvis (isPublic) {scopeString = "offentlig"} annet hvis (isProtected) {scopeString = "beskyttet"} ellers hvis (isPrivate) {scopeString = "privat" } return scopeString} 

Når det ovennevnte Groovy-skriptet kjøres mot klassen og nestede klassen som er vist ovenfor, er utgangen den som vises i neste skjermbilde.

Resultatene av Groovy-skriptet vist i forrige bilde bekrefter hva javap allerede hadde fortalt oss: det er fire syntetiske metoder og en ikke-syntetisk metode definert på den nestede klassen NestedClass. Skriptet forteller oss også at kompilatorgenererte syntetiske metoder er pakke-privat omfang.

Tillegget av syntetiske metoder til den nestede klassen på pakke-privat omfangsnivå er ikke det eneste kompilatoren gjorde i eksemplet ovenfor. Det endret også omfanget av den nestede klassen fra den private innstillingen i kode til pakke-privat i .klasse fil. Faktisk, mens de syntetiske metodene bare ble lagt til i tilfelle der den omsluttende klassen fikk tilgang til det private attributtet, gjør kompilatoren alltid den nestede klassen pakke-privat, selv om den er spesifisert som privat i koden. Den gode nyheten er at dette er en resulterende gjenstand for kompileringsprosessen, noe som betyr at koden ikke kan kompileres som den er mot det endrede omfangsnivået til den nestede klassen eller dens syntetiske metoder. Runtime er der ting kan bli dicey.

Klassen, Rogue, prøver å få tilgang til noen av de syntetiske metodene NestedClass. Kildekoden vises neste, etterfulgt av kompileringsfeilen som ble sett når du prøver å kompilere denne Rogue-kildekoden.

Rogue.java prøver å få tilgang til syntetiske metoder på kompileringstidspunktet

pakke dustin. eksempler; importere statisk java.lang.System.out; public class Rogue {public static void main (final String [] argumenter) {out.println (DemonstrateSyntheticMethods.NestedClass.getDate ()); }} 

Ovennevnte kode vil ikke kompileres, selv for den ikke-syntetiske metoden getDate (), og rapporterer denne feilen:

Buildfile: C: \ java \ eksempler \ syntetisk \ build.xml -init: kompilere: [javac] Kompilering av 1 kildefil til C: \ java \ eksempler \ syntetiske \ klasser [javac] C: \ java \ eksempler \ syntetisk \ src \ dustin \ eksempler \ Rogue.java: 9: dustin.examples.DemonstrateSyntheticMethods.NestedClass har privat tilgang i dustin.examples.DemonstrateSyntheticMethods [javac] out.println (DemonstrateSyntheticMethods.NestedClass.getDate ()); [javac] ^ [javac] 1 feil BYGG MISLYST C: \ java \ eksempler \ syntetisk \ build.xml: 29: Kompilering mislyktes; se kompilatorens feilutgang for detaljer. Total tid: 1 sekund 

Som den ovennevnte kompileringsfeilmeldingen indikerer, er selv den ikke-syntetiske metoden på den nestede klassen utilgjengelig ved kompileringstid fordi den nestede klassen har privat omfang. I sin artikkel Java Insecurities: Accounting for Subtleties That Can Compromise Code diskuterer Charlie Lai potensielle situasjoner der disse kompilatorinnførte endringene er sikkerhetsproblemer. Faisal Feroz går lenger og sier, i innlegget Hvordan skrive sikker Java-kode, "Ikke bruk indre klasser" (se Nested, Inner, Member og Top-Level Classes for detaljer om indre klasser som en delmengde av nestede klasser) .

Mange av oss kan gå lenge i Java-utvikling uten å ha behov for betydelig forståelse av syntetiske metoder. Imidlertid er det situasjoner når bevissthet om disse er viktig. Foruten sikkerhetsproblemer knyttet til disse, er det også å være klar over hva de er når du leser stakkspor. Metodenavn som få tilgang til $ 100, få tilgang til $ 200, få tilgang til $ 300, få tilgang til $ 400, få tilgang til $ 500, få tilgang til $ 600, og få tilgang til $ 1000 i stabelspor reflekterer syntetiske metoder generert av kompilatoren.

Original innlegg Tilgjengelig på //marxsoftware.blogspot.com/

.

Denne historien, "Java's Synthetic Methods" ble opprinnelig utgitt av JavaWorld.

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