Synthetische constructen in Java

1. Overzicht

In deze tutorial zullen we de synthetische constructies van Java bekijken, code die door de compiler is geïntroduceerd om op transparante wijze de toegang tot leden af ​​te handelen die anders onbereikbaar zou zijn vanwege onvoldoende zichtbaarheid of ontbrekende referenties.

Opmerking: vanaf JDK 11 worden synthetische methoden en constructors niet langer gegenereerd, omdat ze worden vervangen door nestgebaseerde toegangscontrole.

2. Synthetisch in Java

De beste definitie van synthetisch we zouden kunnen vinden komt rechtstreeks uit de Java-taalspecificatie (JLS 13.1.7):

Alle constructies die door een Java-compiler worden geïntroduceerd en die geen overeenkomend construct in de broncode hebben, moeten als synthetisch worden gemarkeerd, met uitzondering van de standaardconstructors, de klasse-initialisatiemethode en de waarden en valueOf-methoden van de klasse Enum.

Er zijn verschillende soorten compilatieconstructies, namelijk velden, constructors en methoden. Aan de andere kant, hoewel geneste klassen kunnen worden gewijzigd door de compiler (d.w.z. anonieme klassen), worden ze niet als synthetisch beschouwd.

Laten we, zonder verder oponthoud, dieper ingaan op elk van deze.

3. Synthetische velden

Laten we beginnen met een eenvoudige geneste klasse:

openbare klasse SyntheticFieldDemo {klasse NestedClass {}}

Bij het samenstellen elke innerlijke klasse zal een synthetisch veld bevattendie verwijst naar de klasse van het hoogste niveau. Toevallig is dit wat het mogelijk maakt om toegang te krijgen tot de omringende klasleden vanuit een geneste klas.

Om er zeker van te zijn dat dit gebeurt, zullen we een test implementeren die de geneste klassevelden door reflectie ophaalt en ze controleert met behulp van de isSynthetisch () methode:

openbare ongeldige gegevenSyntheticField_whenIsSynthetic_thenTrue () {Veld [] velden = SyntheticFieldDemo.NestedClass.class .getDeclaredFields (); assertEquals ("Deze klasse mag slechts één veld bevatten", 1, fields.length); voor (Veld f: velden) {System.out.println ("Veld:" + f.getName () + ", isSynthetic:" + f.isSynthetic ()); assertTrue ("Alle velden van deze klasse moeten synthetisch zijn", f.isSynthetic ()); }}

Een andere manier waarop we dit kunnen verifiëren, is door de disassembler via de opdracht uit te voeren javap. In beide gevallen toont de uitvoer een synthetisch veld met de naam deze $ 0.

4. Synthetische methoden

Vervolgens voegen we een privéveld toe aan onze geneste klasse:

openbare klasse SyntheticMethodDemo {klasse NestedClass {privé String nestedField; } public String getNestedField () {retourneer nieuwe NestedClass (). nestedField; } openbare ongeldige setNestedField (String nestedField) {nieuwe NestedClass (). nestedField = nestedField; }}

In dit geval, de compilatie zal accessors voor de variabele genereren. Zonder deze methoden zou het onmogelijk zijn om toegang te krijgen tot een privéveld vanuit de omsluitende instantie.

Nogmaals, we kunnen dit controleren met dezelfde techniek die twee synthetische methoden laat zien die worden genoemd toegang $ 0 en toegang tot $ 1:

openbare leegte gegevenSyntheticMethod_whenIsSynthetic_thenTrue () {Method [] methodes = SyntheticMethodDemo.NestedClass.class .getDeclaredMethods (); assertEquals ("Deze klasse mag slechts twee methoden bevatten", 2, methods.length); voor (Methode m: methodes) {System.out.println ("Methode:" + m.getName () + ", isSynthetic:" + m.isSynthetic ()); assertTrue ("Alle methoden van deze klasse moeten synthetisch zijn", m.isSynthetic ()); }}

Let erop dat om de code te genereren, moet het veld daadwerkelijk worden gelezen of geschreven, anders worden de methoden weg geoptimaliseerd. Dit is de reden waarom we ook een getter en een setter hebben toegevoegd.

Zoals hierboven vermeld, worden deze synthetische methoden niet langer gegenereerd vanaf JDK 11.

4.1. Bridge-methoden

Een speciaal geval van synthetische methoden zijn overbruggingsmethoden, die het wissen van generieke geneesmiddelen behandelen.

Laten we bijvoorbeeld eens kijken naar een simpele Comparator:

openbare klasse BridgeMethodDemo implementeert Comparator {@Override public int Compare (Integer o1, Integer o2) {return 0; }}

Hoewel vergelijken() duurt twee Geheel getal argumenten in de broncode, eenmaal gecompileerd duurt het twee Voorwerp argumenten in plaats daarvan, vanwege het wissen van het type.

Om dit te beheren, de compiler maakt een synthetische brug die zorgt voor het casten van de argumenten:

public int Compare (Object o1, Object o2) {return Compare ((Integer) o1, (Integer) o2); }

Naast onze eerdere tests zullen we dit keer ook bellen isBridge () van de Methode klasse:

openbare ongeldige gegevenBridgeMethod_whenIsBridge_thenTrue () {int synthetischeMethods = 0; Methode [] methodes = BridgeMethodDemo.class.getDeclaredMethods (); voor (Methode m: methoden) {System.out.println ("Methode:" + m.getName () + ", isSynthetic:" + m.isSynthetic () + ", isBridge:" + m.isBridge ()); if (m.isSynthetic ()) {syntheticMethods ++; assertTrue ("De synthetische methode in deze klasse zou ook een bridge-methode moeten zijn", m.isBridge ()); }} assertEquals ("Er zou precies 1 synthetische bridge-methode in deze klasse moeten zijn", 1, syntheticMethods); }

5. Synthetische constructeurs

Ten slotte voegen we een privéconstructor toe:

openbare klasse SyntheticConstructorDemo {privé NestedClass nestedClass = nieuwe NestedClass (); klasse NestedClass {privé NestedClass () {}}}

Deze keer, als we de test of de disassembler hebben uitgevoerd, zullen we zien dat er eigenlijk twee constructors zijn, waarvan er één synthetisch is:

openbare ongeldige gegevenSyntheticConstructor_whenIsSynthetic_thenTrue () {int syntheticConstructors = 0; Constructor [] constructors = SyntheticConstructorDemo.NestedClass .class.getDeclaredConstructors (); assertEquals ("Deze klasse mag slechts twee constructors bevatten", 2, constructors.length); voor (Constructor c: constructors) {System.out.println ("Constructor:" + c.getName () + ", isSynthetic:" + c.isSynthetic ()); if (c.isSynthetic ()) {syntheticConstructors ++; }} assertEquals (1, syntheticConstructors); }

Net als bij de synthetische velden, deze gegenereerde constructor is essentieel om een ​​geneste klasse te instantiëren met een privéconstructor vanuit zijn omsluitende instantie.

Zoals hierboven vermeld, wordt de synthetische constructor niet langer gegenereerd vanaf JDK 11.

6. Conclusie

In dit artikel hebben we synthetische constructies besproken die zijn gegenereerd door de Java-compiler. Om ze te testen hebben we gebruik gemaakt van reflectie, waarover u hier meer kunt lezen.

Zoals altijd is alle code beschikbaar op GitHub.