Synthetische Konstrukte in Java

Synthetische Konstrukte in Java

1. Überblick

In diesem Tutorial werfen wir einen Blick auf die synthetischen Konstrukte von Java, den vom Compiler eingeführten Code, um den Zugriff auf Mitglieder transparent zu handhaben, der sonst aufgrund unzureichender Sichtbarkeit oder fehlender Referenzen nicht erreichbar wäre.

2. Synthetisch in Java

Die beste Definition vonsynthetic, die wir möglicherweise finden könnten, stammt direkt aus der Java-Sprachspezifikation (JLS 13.1.7):

Alle von einem Java-Compiler eingeführten Konstrukte, die kein entsprechendes Konstrukt im Quellcode haben, müssen als synthetisch markiert werden, mit Ausnahme der Standardkonstruktoren, der Klasseninitialisierungsmethode und der Werte und valueOf-Methoden der Enum-Klasse.

Es gibt verschiedene Arten von Kompilierungskonstrukten, nämlich Felder, Konstruktoren und Methoden. Andererseits istalthough nested classes can be altered by the compiler (i.e. anonymous classes), they aren’t considered synthetic.

Lassen Sie uns ohne weiteres tief in diese eintauchen.

3. Synthetische Felder

Beginnen wir mit einer einfachen verschachtelten Klasse:

public class SyntheticFieldDemo {
    class NestedClass {}
}

Beim Kompilierenany inner class will contain a synthetic field which references the top level class. Coincidentally, this is what makes possible to access the enclosing class members from a nested class.

Um sicherzustellen, dass dies geschieht, implementieren wir einen Test, bei dem die verschachtelten Klassenfelder durch Reflektion abgerufen und mit der MethodeisSynthetic()überprüft werden:

public void givenSyntheticField_whenIsSynthetic_thenTrue() {
    Field[] fields = SyntheticFieldDemo.NestedClass.class
      .getDeclaredFields();
    assertEquals("This class should contain only one field",
      1, fields.length);

    for (Field f : fields) {
        System.out.println("Field: " + f.getName() + ", isSynthetic: " +
          f.isSynthetic());
        assertTrue("All the fields of this class should be synthetic",
          f.isSynthetic());
    }
}

Eine andere Möglichkeit, dies zu überprüfen, besteht darin, den Disassembler über den Befehljavap.  auszuführen. In beiden Fällen zeigt die Ausgabe ein synthetisches Feld mit dem Namenthis$0.

4. Synthesemethoden

Als Nächstes fügen wir unserer verschachtelten Klasse ein privates Feld hinzu:

public class SyntheticMethodDemo {
    class NestedClass {
        private String nestedField;
    }

    public String getNestedField() {
        return new NestedClass().nestedField;
    }

    public void setNestedField(String nestedField) {
        new NestedClass().nestedField = nestedField;
    }
}

In diesem Fallthe compilation will generate accessors to the variable. Without these methods, it’d be impossible to access a private field from the enclosing instance.

Wir können dies noch einmal mit derselben Technik überprüfen, die zwei Synthesemethoden zeigt, die alsaccess$0 undaccess$1 bezeichnet werden:

public void givenSyntheticMethod_whenIsSynthetic_thenTrue() {
    Method[] methods = SyntheticMethodDemo.NestedClass.class
      .getDeclaredMethods();
    assertEquals("This class should contain only two methods",
      2, methods.length);

    for (Method m : methods) {
        System.out.println("Method: " + m.getName() + ", isSynthetic: " +
          m.isSynthetic());
        assertTrue("All the methods of this class should be synthetic",
          m.isSynthetic());
    }
}

Beachten Sie, dassin order to generate the code, the field must actually be read from or written to,otherwise, the methods will be optimized away. Aus diesem Grund haben wir auch einen Getter und einen Setter hinzugefügt.

4.1. Brückenmethoden

Ein Sonderfall synthetischer Methoden sind Brückenmethoden, die das Löschen von Generika behandeln.

Betrachten wir zum Beispiel ein einfaches Comparator:

public class BridgeMethodDemo implements Comparator {
    @Override
    public int compare(Integer o1, Integer o2) {
        return 0;
    }
}

Obwohlcompare() zweiInteger Argumente in der Quelle akzeptiert, werden nach dem Kompilieren aufgrund der Typlöschung stattdessen zweiObject Argumente verwendet.

Um dies zu verwalten,the compiler creates a synthetic bridge which takes care of casting the arguments:

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

Zusätzlich zu unseren vorherigen Tests rufen wir dieses Mal auchisBridge() aus der KlasseMethod auf:

public void givenBridgeMethod_whenIsBridge_thenTrue() {
    int syntheticMethods = 0;
    Method[] methods = BridgeMethodDemo.class.getDeclaredMethods();
    for (Method m : methods) {
        System.out.println("Method: " + m.getName() + ", isSynthetic: " +
          m.isSynthetic() + ", isBridge: " + m.isBridge());
        if (m.isSynthetic()) {
            syntheticMethods++;
            assertTrue("The synthetic method in this class should also be a bridge method",
              m.isBridge());
        }
    }
    assertEquals("There should be exactly 1 synthetic bridge method in this class",
      1, syntheticMethods);
}

5. Synthetische Konstruktoren

Schließlich fügen wir einen privaten Konstruktor hinzu:

public class SyntheticConstructorDemo {
    private NestedClass nestedClass = new NestedClass();

    class NestedClass {
        private NestedClass() {}
    }
}

Dieses Mal, wenn wir den Test oder den Disassembler ausführen, werden wir feststellen, dass es tatsächlich zwei Konstruktoren gibt, von denen einer synthetisch ist:

public void givenSyntheticConstructor_whenIsSynthetic_thenTrue() {
    int syntheticConstructors = 0;
    Constructor[] constructors = SyntheticConstructorDemo.NestedClass
      .class.getDeclaredConstructors();
    assertEquals("This class should contain only two constructors",
      2, constructors.length);

    for (Constructor c : constructors) {
        System.out.println("Constructor: " + c.getName() +
          ", isSynthetic: " + c.isSynthetic());

        if (c.isSynthetic()) {
            syntheticConstructors++;
        }
    }

    assertEquals(1, syntheticConstructors);
}

Ähnlich wie bei den synthetischen Feldernthis generated constructor is essential to instantiate a nested class with a private constructor from its enclosing instance.

6. Fazit

In diesem Artikel haben wir synthetische Konstrukte behandelt, die vom Java-Compiler generiert wurden. Um sie zu testen, haben wir die Reflexion verwendet, mit der Sie mehr überhere erfahren können.

Wie immer ist der gesamte Codeover on GitHub verfügbar.