Синтетические конструкции в Java

Синтетические конструкции в Java

1. обзор

В этом руководстве мы рассмотрим синтетические конструкции Java, код, введенный компилятором для прозрачной обработки доступа к членам, которые в противном случае были бы недоступны из-за недостаточной видимости или отсутствия ссылок.

2. Синтетика в Java

Лучшее определениеsynthetic, которое мы могли бы найти, взято непосредственно из спецификации языка Java (JLS 13.1.7):

Любые конструкции, представленные компилятором Java, которые не имеют соответствующей конструкции в исходном коде, должны быть помечены как синтетические, за исключением конструкторов по умолчанию, метода инициализации класса, а также методов значения и valueOf класса Enum.

Существуют различные виды конструкций компиляции, а именно поля, конструкторы и методы. С другой стороны,although nested classes can be altered by the compiler (i.e. anonymous classes), they aren’t considered synthetic.

Без лишних слов, давайте углубимся в каждую из них.

3. Синтетические поля

Начнем с простого вложенного класса:

public class SyntheticFieldDemo {
    class NestedClass {}
}

При компиляцииany 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.

Чтобы убедиться, что именно это происходит, мы реализуем тест, который получает поля вложенного класса путем отражения и проверяет их с помощью методаisSynthetic():

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());
    }
}

Другой способ проверить это - запустить дизассемблер с помощью командыjavap. . В любом случае вывод показывает синтетическое поле с именемthis$0.

4. Синтетические методы

Затем мы добавим частное поле к нашему вложенному классу:

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

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

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

В этом случаеthe compilation will generate accessors to the variable. Without these methods, it’d be impossible to access a private field from the enclosing instance.

Еще раз, мы можем проверить это с помощью той же техники, которая показывает два синтетических метода, называемыхaccess$0 иaccess$1:

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());
    }
}

Обратите внимание, чтоin order to generate the code, the field must actually be read from or written to,otherwise, the methods will be optimized away. Это причина, почему мы также добавили геттер и сеттер.

4.1. Мостовые методы

Особым случаем синтетических методов являются методы-мосты, которые обрабатывают стирание типа обобщений.

Например, давайте рассмотрим простой Comparator:

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

Хотяcompare() принимает два аргументаInteger в исходном коде, после компиляции вместо них он принимает два аргументаObject из-за стирания типа.

Чтобы управлять этим,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);
}

В дополнение к нашим предыдущим тестам, на этот раз мы также вызовемisBridge() из классаMethod:

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. Синтетические конструкторы

Наконец, мы добавим частный конструктор:

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

    class NestedClass {
        private NestedClass() {}
    }
}

На этот раз, как только мы запустим тест или дизассемблер, мы увидим, что на самом деле существует два конструктора, один из которых синтетический:

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);
}

Аналогично синтетическим полямthis generated constructor is essential to instantiate a nested class with a private constructor from its enclosing instance.

6. Заключение

В этой статье мы обсудили синтетические конструкции, сгенерированные компилятором Java. Чтобы проверить их, мы использовали отражение, о котором вы можете узнать больше оhere.

Как всегда доступен весь кодover on GitHub.