Syntetiske konstruktioner i Java

1. Oversigt

I denne tutorial tager vi et kig på Java's syntetiske konstruktioner, kode, der er introduceret af compileren for transparent håndtering af adgang til medlemmer, som ellers ville være utilgængelige på grund af utilstrækkelig synlighed eller manglende referencer.

Bemærk: startende med JDK 11 genereres ikke syntetiske metoder og konstruktører længere, da de erstattes af redenbaseret adgangskontrol.

2. Syntetisk i Java

Den bedste definition af syntetisk vi muligvis kan finde kommer direkte fra Java Language Specification (JLS 13.1.7):

Alle konstruktioner, der er introduceret af en Java-kompilator, og som ikke har en tilsvarende konstruktion i kildekoden, skal markeres som syntetiske, undtagen standardkonstruktører, klassens initialiseringsmetode og værdierne og værdien af ​​Enum-klassen.

Der er forskellige slags kompileringskonstruktioner, nemlig felter, konstruktører og metoder. På den anden side, skønt indlejrede klasser kan ændres af kompilatoren (dvs. anonyme klasser), betragtes de ikke som syntetiske.

Uden yderligere ado, lad os dykke dybt ned i hver af disse.

3. Syntetiske felter

Lad os begynde med en simpel indlejret klasse:

offentlig klasse SyntheticFieldDemo {klasse NestedClass {}}

Når det er kompileret, enhver indre klasse vil indeholde et syntetisk feltder henviser til topklassen. Tilfældigvis er dette det, der gør det muligt at få adgang til de indesluttede klassemedlemmer fra en indlejret klasse.

For at sikre, at dette er hvad der sker, implementerer vi en test, der får de indlejrede klassefelter ved refleksion og kontrollerer dem ved hjælp af isSynthetic () metode:

public void givenSyntheticField_whenIsSynthetic_thenTrue () {Field [] fields = SyntheticFieldDemo.NestedClass.class .getDeclaredFields (); assertEquals ("Denne klasse skal kun indeholde et felt", 1, fields.length); for (Felt f: felter) {System.out.println ("Felt:" + f.getName () + ", isSynthetic:" + f.isSynthetic ()); assertTrue ("Alle felter i denne klasse skal være syntetiske", f.isSynthetic ()); }}

En anden måde, vi kunne kontrollere dette på, var ved at køre disassembleren gennem kommandoen javap. I begge tilfælde viser output et syntetisk felt med navnet denne $ 0.

4. Syntetiske metoder

Dernæst tilføjer vi et privat felt til vores indlejrede klasse:

offentlig klasse SyntheticMethodDemo {klasse NestedClass {privat streng nestedField; } offentlig String getNestedField () {returner ny NestedClass (). nestedField; } public void setNestedField (String nestedField) {new NestedClass (). nestedField = nestedField; }}

I dette tilfælde, kompileringen vil generere accessors til variablen. Uden disse metoder ville det være umuligt at få adgang til et privat felt fra den indesluttende forekomst.

Endnu en gang kan vi kontrollere dette med den samme teknik, der viser to syntetiske metoder kaldet få adgang til $ 0 og få adgang til $ 1:

offentlig ugyldighed givenSyntheticMethod_whenIsSynthetic_thenTrue () {Method [] methods = SyntheticMethodDemo.NestedClass.class .getDeclaredMethods (); assertEquals ("Denne klasse skal kun indeholde to metoder", 2, methods.length); for (Metode m: metoder) {System.out.println ("Metode:" + m.getName () + ", isSynthetic:" + m.isSynthetic ()); assertTrue ("Alle metoderne i denne klasse skal være syntetiske", m.isSynthetic ()); }}

Læg mærke til det for at generere koden skal feltet faktisk læses fra eller skrives til, Ellers optimeres metoderne væk. Dette er grunden til, at vi også tilføjede en getter og en setter.

Som nævnt ovenfor genereres disse syntetiske metoder ikke længere startende med JDK 11.

4.1. Brometoder

Et specielt tilfælde af syntetiske metoder er brometoder, der håndterer sletning af generiske stoffer.

Lad os for eksempel overveje en simpel Komparator:

offentlig klasse BridgeMethodDemo implementerer Comparator {@ Override public int compare (Integer o1, Integer o2) {return 0; }}

Selvom sammenligne() tager to Heltal argumenter i kilden, når de først er samlet, tager det to Objekt argumenter i stedet på grund af sletning af typen.

For at klare dette, kompilatoren opretter en syntetisk bro, der tager sig af at kaste argumenterne:

offentlig int sammenligne (Objekt o1, Objekt o2) {return sammenligne ((Heltal) o1, (Heltal) o2); }

Ud over vores tidligere tests ringer vi også denne gang isBridge () fra Metode klasse:

offentlig ugyldighed givenBridgeMethod_whenIsBridge_thenTrue () {int syntetiskMethods = 0; Metode [] metoder = BridgeMethodDemo.class.getDeclaredMethods (); til (Metode m: metoder) {System.out.println ("Metode:" + m.getName () + ", isSynthetic:" + m.isSynthetic () + ", isBridge:" + m.isBridge ()); hvis (m.isSynthetic ()) {syntetiske metoder ++; assertTrue ("Den syntetiske metode i denne klasse skal også være en brometode", m.isBridge ()); }} assertEquals ("Der skal være nøjagtigt 1 syntetisk brometode i denne klasse", 1, syntetiske metoder); }

5. Syntetiske konstruktører

Endelig tilføjer vi en privat konstruktør:

offentlig klasse SyntheticConstructorDemo {private NestedClass nestedClass = ny NestedClass (); klasse NestedClass {private NestedClass () {}}}

Denne gang, når vi har kørt testen eller demontereren, ser vi, at der faktisk er to konstruktører, hvoraf den ene er syntetisk:

offentlig ugyldighed givenSyntheticConstructor_whenIsSynthetic_thenTrue () {int syntetiskConstructors = 0; Constructor [] constructors = SyntheticConstructorDemo.NestedClass .class.getDeclaredConstructors (); assertEquals ("Denne klasse skal kun indeholde to konstruktører", 2, konstruktører. længde); til (Constructor c: constructors) {System.out.println ("Constructor:" + c.getName () + ", isSynthetic:" + c.isSynthetic ()); hvis (c.isSynthetic ()) {syntetiske konstruktører ++; }} assertEquals (1, syntetiske konstruktører); }

På samme måde som de syntetiske felter, denne genererede konstruktør er vigtig for at instansiere en indlejret klasse med en privat konstruktør fra dens indesluttende forekomst.

Som nævnt ovenfor genereres den syntetiske konstruktør ikke længere startende med JDK 11.

6. Konklusion

I denne artikel diskuterede vi syntetiske konstruktioner genereret af Java-kompilatoren. For at teste dem brugte vi refleksion, som du kan lære mere om her.

Som altid er al koden tilgængelig på GitHub.


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