Статьи

Java Secret: сгенерированные методы

 Компилятор Java генерирует дополнительные методы, которые появляются в следах стека. Сначала это может сбить с толку. Кто они и почему они существуют?

Модель доступа в JVM

Модель доступа в JVM существенно не изменилась с версии 1.0. Однако в Java 5.0 добавлены такие функции, как внутренние / вложенные классы и конвариантные возвращаемые типы, которых не было в оригинальном проекте. Так как они поддерживаются?

Проблема с вложенными классами

Вложенные классы могут обращаться к закрытым методам и полям внешнего класса. Однако JVM не поддерживает это, поэтому компилятор генерирует методы в качестве обходного пути.

Частные Методы

В этом примере внутренний класс вызывает закрытый метод
init () внешнего класса.

public class PrivateMethod { // line 4
    private void init() {
        throw new UnsupportedOperationException(); // line 6
    };

    class Inner {
        Inner() {
            init();
        }
    }

     public static void main(String... args) {
        PrivateMethod pm = new PrivateMethod();
        Inner inner = pm.new Inner();
    }
}

Так как же выглядит трассировка стека, когда мы запускаем это?

Exception in thread "main" java.lang.UnsupportedOperationException
 at com.google.code.java.core.javac.generated.PrivateMethod.init(PrivateMethod.java:6)
 at com.google.code.java.core.javac.generated.PrivateMethod.access$000(PrivateMethod.java:4)
 at com.google.code.java.core.javac.generated.PrivateMethod$Inner.(PrivateMethod.java:11)
 at com.google.code.java.core.javac.generated.PrivateMethod.main(PrivateMethod.java:17)

В трассировке стека есть метод с именем
access $ 000 в строке 4, которая является первой строкой класса. Это настоящий метод? Что мы видим в байт-коде?

$ javap -c -private -classpath . com.google.code.java.core.javac.generated.PrivateMethod

static void <b>access$000</b>(com.google.code.java.core.javac.generated.PrivateMethod);
  Code:
   0:   aload_0
   1:   invokespecial   #1; //Method init:()V
   4:   return

$ javap -c -private -classpath . com.google.code.java.core.javac.generated.PrivateMethod\$Inner
Compiled from "PrivateMethod.java"
public class com.google.code.java.core.javac.generated.PrivateMethod$Inner extends java.lang.Object{
final com.google.code.java.core.javac.generated.PrivateMethod this$0;

com.google.code.java.core.javac.generated.PrivateMethod$Inner(com.google.code.java.core.javac.generated.PrivateMethod);
  Code:
   0:   aload_0
   1:   aload_1
   2:   putfield        #1; //Field this$0:Lcom/google/code/java/core/javac/generated/PrivateMethod;
   5:   aload_0
   6:   invokespecial   #2; //Method java/lang/Object."":()V
   9:   aload_1
   10:  invokestatic    #3; //Method com/google/code/java/core/javac/generated/PrivateMethod.
                        <b>access$000</b>:(Lcom/google/code/java/core/javac/generated/PrivateMeth
   13:  return

}

Компилятор добавил статический метод, который не является закрытым, поэтому класс Inner может косвенно обращаться к закрытому методу внешнего класса.

Частные поля

Доступ к закрытым полям осуществляется через методы получения и установки, но компилятор может генерировать методы для операций присваивания, такие как
++ и
* =

public class PrivateField {
    private int num = 0;

    public class Inner {
        public void set(int n) {
            num = n;
        }

        public int get() {
            return num;
        }

        public void increment() {
            num++;
        }

        public void multiply(int n) {
            num *= n;
        }
    }
}

Компилируется с четырьмя сгенерированными методами, по одному для каждого из get, set, ++ и * =

$ javap -c -private -classpath . com.google.code.java.core.javac.generated.PrivateField
<b>setter</b>
static int access$002(com.google.code.java.core.javac.generated.PrivateField, int);
  Code:
   0:   aload_0
   1:   iload_1
   2:   dup_x1
   3:   putfield        #1; //Field num:I
   6:   ireturn

<b>getter</b>
static int access$000(com.google.code.java.core.javac.generated.PrivateField);
  Code:
   0:   aload_0
   1:   getfield        #1; //Field num:I
   4:   ireturn

increment ++
static int access$008(com.google.code.java.core.javac.generated.PrivateField);
  Code:
   0:   aload_0
   1:   dup
   2:   getfield        #1; //Field num:I
   5:   dup_x1
   6:   iconst_1
   7:   iadd
   8:   putfield        #1; //Field num:I
   11:  ireturn

multiplier *=
static int access$028(com.google.code.java.core.javac.generated.PrivateField, int);
  Code:
   0:   aload_0
   1:   dup
   2:   getfield        #1; //Field num:I
   5:   iload_1
   6:   imul
   7:   dup_x1
   8:   putfield        #1; //Field num:I
   11:  ireturn

Ковариантные типы возврата

Ковариантные возвращаемые типы — это функция, которая была добавлена ​​в Java 5.0, но не поддерживается в JVM. По этой причине компилятор генерирует метод, который переопределяет все супер методы и их возвращаемые типы.

public class CovariantReturnTypeA {
    public Object method() { return 1L; }
}

public class CovariantReturnTypeB extends CovariantReturnTypeA {
    public Number method() { return 2.0; }
}

public class CovariantReturnTypeC extends CovariantReturnTypeB {
    public Integer method() { return 3; }
}

Последний метод () переопределяет метод, который возвращает Number, и метод, который возвращает Object. Чтобы реализовать это, компиляция генерирует метод с той же сигнатурой, который вызывает метод, который возвращает Integer

$ javap -c -private -classpath . com.google.code.java.core.javac.generated.CovariantReturnTypeC

public java.lang.Integer method();
  Code:
   0:   iconst_3
   1:   invokestatic    #2; //Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
   4:   areturn

public java.lang.Number method();
  Code:
   0:   aload_0
   1:   invokevirtual   #3; //Method method:()Ljava/lang/Integer;
   4:   areturn

public java.lang.Object method();
  Code:
   0:   aload_0
   1:   invokevirtual   #3; //Method method:()Ljava/lang/Integer;
   4:   areturn

В байт-коде есть три реализации метода ().

Последствия для производительности

Влияние производительности на Java Standard Edition невелико, поскольку эти методы могут быть встроенными и эффективно не оказывать влияния. В Java Mobile Edition это может быть не так.

Они могут вызвать некоторую путаницу, и если это проблема, вы можете вместо этого сделать поля / методы локальными для пакета, и сгенерированные методы исчезнут.

От http://vanillajava.blogspot.com/2011/07/java-secret-generated-methods.html