Programmering

Java's syntetiske metoder

I dette blogindlæg ser jeg på begrebet syntetiske Java-metoder. Indlægget opsummerer, hvad en Java-syntetisk metode er, hvordan man kan oprette og identificere, og konsekvenserne af Java-syntetiske metoder på Java-udvikling.

Java Language Specification (afsnit 13.1) siger "Alle konstruktioner, der er introduceret af compileren, og som ikke har en tilsvarende konstruktion i kildekoden, skal markeres som syntetiske, undtagen standardkonstruktører og klassens initialiseringsmetode." Yderligere spor om betydningen af ​​syntetisk i Java findes i Javadoc-dokumentationen til Member.isSynthetic (). Metodens dokumentation siger, at den returnerer "sandt, hvis og kun hvis dette medlem blev introduceret af kompilatoren." Jeg kan godt lide den meget korte definition af "syntetisk": en Java-konstruktion introduceret af compileren.

Java-compileren skal oprette syntetiske metoder på indlejrede klasser, når deres attributter, der er specificeret med den private modifikator, er tilgængelige af den vedlagte klasse. Den næste kodeeksempel angiver denne situation.

DemonstrateSyntheticMethods.java (vedlagte klasse påkalder en indlejret klasse privat attribut)

pakke dustin. eksempler; import java.util.Calendar; importere statisk java.lang.System.out; public final class DemonstrateSyntheticMethods {public static void main (final String [] argumenter) {DemonstrateSyntheticMethods.NestedClass nested = new DemonstrateSyntheticMethods.NestedClass (); out.println ("String:" + nestet.highlyConfidential); } privat statisk slutklasse NestedClass {private String highlyConfidential = "Fortæl ingen om mig"; private int highlyConfidentialInt = 42; privat kalender highlyConfidentialCalendar = Calendar.getInstance (); privat boolsk megetConfidentialBoolean = sand; }} 

Ovenstående kode kompileres uden hændelser. Når javap køres mod det kompilerede .klasse fil, er output som vist i det følgende skærmbillede.

Som ovenstående skærmbillede angiver, er en syntetisk metode med navnet få adgang til $ 100 er oprettet på den indlejrede klasse NestedClass at levere sin private streng til den lukkende klasse. Bemærk, at den syntetiske metode kun tilføjes for den enkelte private attribut for NestedClass, som den omsluttende klasse har adgang til. Hvis jeg ændrer den vedlagte klasse for at få adgang til alle private attributter for NestedClass, genereres yderligere syntetiske metoder. Det næste kodeeksempel demonstrerer at gøre netop dette, og skærmbillede-snapshotet efter det viser, at der i dette tilfælde genereres fire syntetiske metoder.

DemonstrateSyntheticMethods.java (vedlagte klasse påkalder fire indlejrede klasse private attributter)

pakke dustin. eksempler; import java.util.Calendar; importere statisk java.lang.System.out; offentlig endelig klasse 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 slutklasse NestedClass {private String highlyConfidential = "Fortæl ingen om mig"; private int highlyConfidentialInt = 42; privat kalender highlyConfidentialCalendar = Calendar.getInstance (); privat boolsk megetConfidentialBoolean = sand; }} 

Som de to foregående kodekodestykker ovenfor og de tilknyttede billeder viser, introducerer Java-kompilatoren syntetiske metoder efter behov. Når kun den ene af den indlejrede klasses private attributter blev åbnet af den indesluttende klasse, var der kun en syntetisk metode (få adgang til $ 100) blev oprettet af kompilatoren. Når alle fire private attributter i den indlejrede klasse blev åbnet af den indesluttende klasse, blev der imidlertid genereret fire tilsvarende syntetiske metoder af kompilatoren (få adgang til $ 100, få adgang til $ 200, få adgang til $ 300og få adgang til $ 400).

I alle tilfælde af en lukkende klasse, der har adgang til den indlejrede klasses private data, blev der oprettet en syntetisk metode, der gjorde det muligt for denne adgang at ske. Hvad sker der, når den indlejrede klasse giver en accessor til sine private data, som den vedlagte klasse kan bruge? Dette demonstreres i den næste kodeliste og i dens output som vist i det næste skærmbillede.

DemonstrateSyntheticMethods.java med Nested Class Public Accessor til private data

pakke dustin. eksempler; import java.util.Calendar; importere java.util.Date; importere statisk java.lang.System.out; public final class 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 slutklasse NestedClass {private String highlyConfidential = "Fortæl ingen om mig"; private int highlyConfidentialInt = 42; privat kalender highlyConfidentialCalendar = Calendar.getInstance (); privat boolsk megetConfidentialBoolean = sand; privat dato dato = ny dato (); public Date getDate () {return this.date; }}} 

Ovenstående skærmbillede viser, at compileren ikke behøvede at generere en syntetisk metode til at få adgang til den private Date-attribut i den indlejrede klasse, fordi den vedlagte klasse fik adgang til den attribut via den angivne getDate () metode. Selv med getDate () forudsat, ville compileren have genereret en syntetisk metode til at få adgang til dato er den vedlagte kode blevet skrevet for at få adgang til dato attribut direkte (som en ejendom) snarere end via accessor-metoden.

Det sidste skærmbillede viser en ny observation. Som den nyligt tilføjede getDate () Metoden viser i det skærmbillede øjebliksbillede, modifikatorer som f.eks offentlig er inkluderet i javap-output. Da der ikke vises nogen modifikator for de syntetiske metoder, der er oprettet af compileren, ved vi, at de er pakkeniveau (eller pakke-private). Kort sagt har kompilatoren oprettet pakke-private metoder til at få adgang til private attributter.

Java-refleksions-API'erne giver en anden tilgang til bestemmelse af syntetiske metoder. Den næste kodeliste er for et Groovy-script, der bruger Java-refleksions-API'erne til bekvemt at give detaljer om metoderne til den indlejrede klasse vist ovenfor.

reflectOnMethods.groovy

#! / usr / bin / env groovy import java.lang.reflect.Methode import java.lang.reflect.Modifier if (args == null || args.size () <2) {println "Ydre og indlejrede klassenavne skal leveres. " println "\ nBrug # 1: reflektererOnMethods kvalificeretOuterClassName nestedClassName \ n" println "\ nUsage # 2: groovy -cp classpath reflectOnMethods.groovy QualifiedOuterClassName nestedClassName \ n" println "\ t1. Inkluder ydre og indlejrede klasser på classp t2. Inkluder IKKE \ $ foran det indlejrede klassenavn. \ 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 "Kan ikke find indlejret klasse $ {fullNestedClassName} "System.exit (-2)} // Brug deklareretMetoder, fordi du er ligeglad med nedarvede metoder nestedClass.declaredMethods.each {print" \ nMethod '$ {it.name}' "print" er $ {getScopeModifier (it)} rækkevidde, "udskriv" $ {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 (modifikatorer) String scopeString = "pakke-privat" // standard hvis (isPublic) {scopeString = "offentlig"} ellers hvis (isProtected) {scopeString = "beskyttet"} ellers hvis (isPrivate) {scopeString = "privat" } returner scopeString} 

Når ovenstående Groovy-script udføres mod klassen og den indlejrede klasse, der er vist ovenfor, er output det, der vises i det næste skærmbillede.

Resultaterne af Groovy-scriptet vist i det forrige billede bekræfter, hvad javap allerede havde fortalt os: der er fire syntetiske metoder og en ikke-syntetisk metode defineret på den indlejrede klasse NestedClass. Scriptet fortæller os også, at de compiler-genererede syntetiske metoder er pakke-private omfang.

Tilføjelsen af ​​syntetiske metoder til den indlejrede klasse på pakke-privat omfangsniveau er ikke det eneste, kompilatoren gjorde i ovenstående eksempel. Det ændrede også omfanget af selve den indlejrede klasse fra den private indstilling i kode til pakke-privat i .klasse fil. Selvom de syntetiske metoder kun blev tilføjet i det tilfælde, hvor den omsluttende klasse fik adgang til den private attribut, gør compileren altid den indlejrede klasse pakke-privat, selvom den er angivet som privat i koden. Den gode nyhed er, at dette er en resulterende artefakt af kompileringsprocessen, hvilket betyder, at kode ikke kan kompileres som den er mod det ændrede omfangsniveau for den indlejrede klasse eller dens syntetiske metoder. Runtime er, hvor tingene kan blive dicey.

Klassen, Rogue, forsøger at få adgang til nogle af de syntetiske metoder NestedClass. Dens kildekode vises derefter efterfulgt af compilerfejlen, der ses, når man prøver at kompilere denne Rogue-kildekode.

Rogue.java forsøger at få adgang 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 ()); }} 

Ovenstående kode kompileres ikke, selv for den ikke-syntetiske metode getDate ()og rapporterer denne fejl:

Buildfile: C: \ java \ eksempler \ syntetisk \ build.xml -init: kompilering: [javac] Kompilering af 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 adgang i dustin.examples.DemonstrateSyntheticMethods [javac] out.println (DemonstrateSyntheticMethods.NestedClass.getDate ()); [javac] ^ [javac] 1 fejl BUILD FAILED C: \ java \ eksempler \ syntetisk \ build.xml: 29: Kompilering mislykkedes; se compilerfejloutputtet for detaljer. Samlet tid: 1 sekund 

Som ovenstående kompileringsfejlmeddelelse indikerer, er selv den ikke-syntetiske metode på den indlejrede klasse utilgængelig ved kompileringstidspunktet fordi den indlejrede klasse har privat anvendelsesområde. I sin artikel Java Insecurities: Accounting for Subtleties That Can Compromise Code diskuterer Charlie Lai potentielle situationer, hvor disse compiler-introducerede ændringer er sikkerhedssårbarheder. Faisal Feroz går videre og siger, i indlægget Hvordan man skriver en sikker Java-kode, "Brug ikke indre klasser" (se Indlejrede, indvendige, medlems- og topniveauklasser for detaljer om indre klasser som en delmængde af indlejrede klasser) .

Mange af os kan gå i lang tid i Java-udvikling uden at have brug for betydelig forståelse af syntetiske metoder. Der er dog situationer, hvor bevidsthed om disse er vigtig. Udover sikkerhedsproblemer relateret til disse er det også at være opmærksom på, hvad de er, når man læser stakspor. Metodenavne som f.eks få adgang til $ 100, få adgang til $ 200, få adgang til $ 300, få adgang til $ 400, få adgang til $ 500, få adgang til $ 600og få adgang til $ 1000 i stacksporingen afspejler syntetiske metoder genereret af compileren.

Oprindeligt indlæg tilgængelig på //marxsoftware.blogspot.com/

.

Denne historie, "Java's Synthetic Methods" blev oprindeligt udgivet af JavaWorld.