1. Обзор
В этом руководстве мы рассмотрим синтетические конструкции Java, код, введенный компилятором для прозрачной обработки доступа к членам, которые в противном случае были бы недоступны из-за недостаточной видимости или отсутствия ссылок.
Примечание: начиная с JDK 11 синтетические методы и конструкторы больше не генерируются, поскольку они заменены управлением доступом на основе гнезд .
2. Синтетика в Java
Лучшее определение синтетического
, которое мы могли бы найти, исходит непосредственно из Спецификации языка Java ( JLS 13.1.7 ):
Любые конструкции, введенные компилятором Java, которые не имеют соответствующей конструкции в исходном коде, должны быть помечены как синтетические, за исключением конструкторов по умолчанию, метода инициализации класса и методов values и valueOf класса Enum.
Существуют различные типы конструкций компиляции, а именно поля, конструкторы и методы. С другой стороны, хотя компилятор может изменять вложенные классы (т. е. анонимные классы), они не считаются синтетическими .
Без дальнейших церемоний, давайте углубимся в каждый из них.
3. Синтетические поля
Начнем с простого вложенного класса:
public class SyntheticFieldDemo {
class NestedClass {}
}
При компиляции любой внутренний класс будет содержать синтетическое поле `` , которое ссылается на класс верхнего уровня. Кстати, именно это позволяет получить доступ к членам окружающего класса из вложенного класса.
Чтобы убедиться, что это именно то, что происходит, мы реализуем тест, который получает поля вложенного класса путем отражения и проверяет их с помощью метода 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;
}
}
В этом случае компиляция сгенерирует методы доступа к переменной. Без этих методов было бы невозможно получить доступ к частному полю из включающего экземпляра.
Еще раз, мы можем проверить это с помощью той же техники, которая показывает два синтетических метода с именами 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());
}
}
Обратите внимание, что для генерации кода поле должно быть фактически прочитано или записано в , иначе методы будут оптимизированы . По этой причине мы также добавили геттер и сеттер.
Как упоминалось выше, эти синтетические методы больше не генерируются, начиная с JDK 11.
4.1. Мостовые методы
Особым случаем синтетических методов являются промежуточные методы, которые обрабатывают стирание типов дженериков.
Например, давайте рассмотрим простой Comparator
:
public class BridgeMethodDemo implements Comparator<Integer> {
@Override
public int compare(Integer o1, Integer o2) {
return 0;
}
}
Несмотря на то, что compare()
принимает два аргумента типа Integer
в исходном коде, после компиляции вместо этого он принимает два аргумента типа Object из-за стирания типа.
Чтобы справиться с этим, компилятор создает синтетический мост, который заботится о приведении аргументов :
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);
}
Подобно синтетическим полям, этот сгенерированный конструктор необходим для создания экземпляра вложенного класса с закрытым конструктором из его включающего экземпляра.
Как упоминалось выше, синтетический конструктор больше не создается, начиная с JDK 11.
6. Заключение
В этой статье мы обсудили синтетические конструкции, созданные компилятором Java. Чтобы их протестировать, мы использовали рефлексию, о которой вы можете узнать подробнее здесь .
Как всегда, весь код доступен на GitHub .