Enum, разобранный с помощью javap, не показывает аргументы конструктора

Когда я разбираю перечисление с помощью javap, неявные аргументы конструктора перечисления отсутствуют, и я не могу понять, почему.

Вот перечисление:

enum Foo { X }

Я компилирую и разбираю это (на Java 8u60) с помощью этой команды:

javac Foo.java && javap -c -p Foo

И вот результат, который я получаю:

final class Foo extends java.lang.Enum<Foo> {
  public static final Foo X;

  private static final Foo[] $VALUES;

  public static Foo[] values();
    Code:
       0: getstatic     #1                  // Field $VALUES:[LFoo;
       3: invokevirtual #2                  // Method "[LFoo;".clone:()Ljava/lang/Object;
       6: checkcast     #3                  // class "[LFoo;"
       9: areturn

  public static Foo valueOf(java.lang.String);
    Code:
       0: ldc           #4                  // class Foo
       2: aload_0
       3: invokestatic  #5                  // Method java/lang/Enum.valueOf:(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum;
       6: checkcast     #4                  // class Foo
       9: areturn

  private Foo(); // <--- here
    Code:
       0: aload_0
       1: aload_1
       2: iload_2
       3: invokespecial #6                  // Method java/lang/Enum."<init>":(Ljava/lang/String;I)V
       6: return

  static {};
    Code:
       0: new           #4                  // class Foo
       3: dup
       4: ldc           #7                  // String X
       6: iconst_0
       7: invokespecial #8                  // Method "<init>":(Ljava/lang/String;I)V
      10: putstatic     #9                  // Field X:LFoo;
      13: iconst_1
      14: anewarray     #4                  // class Foo
      17: dup
      18: iconst_0
      19: getstatic     #9                  // Field X:LFoo;
      22: aastore
      23: putstatic     #1                  // Field $VALUES:[LFoo;
      26: return
}

Я путаюсь с приватным конструктором, который используется для создания каждой константы перечисления. Разборка показывает, что она не требует аргументов (private Foo();), но это, безусловно, принимает аргументы. Например, вы можете увидеть load инструкции, читающие переданное имя константы enum и порядковый номер, а также this указатель и передача их конструктору суперкласса, который требует их. Код в блоке статического инициализатора также показывает, что он помещает эти аргументы в стек перед вызовом конструктора.

Теперь я предположил бы, что это была просто неясная ошибка в javap, но когда я компилирую точно такое же перечисление с компилятором Eclipse и разбираю, что с использованием javap, конструктор точно такой же, за исключением показанных аргументов:

final class Foo extends java.lang.Enum<Foo> {
  public static final Foo X;

  private static final Foo[] ENUM$VALUES;

  static {};
    Code:
       0: new           #1                  // class Foo
       3: dup
       4: ldc           #12                 // String X
       6: iconst_0
       7: invokespecial #13                 // Method "<init>":(Ljava/lang/String;I)V
      10: putstatic     #17                 // Field X:LFoo;
      13: iconst_1
      14: anewarray     #1                  // class Foo
      17: dup
      18: iconst_0
      19: getstatic     #17                 // Field X:LFoo;
      22: aastore
      23: putstatic     #19                 // Field ENUM$VALUES:[LFoo;
      26: return

  private Foo(java.lang.String, int); // <--- here
    Code:
       0: aload_0
       1: aload_1
       2: iload_2
       3: invokespecial #23                 // Method java/lang/Enum."<init>":(Ljava/lang/String;I)V
       6: return

  public static Foo[] values();
    Code:
       0: getstatic     #19                 // Field ENUM$VALUES:[LFoo;
       3: dup
       4: astore_0
       5: iconst_0
       6: aload_0
       7: arraylength
       8: dup
       9: istore_1
      10: anewarray     #1                  // class Foo
      13: dup
      14: astore_2
      15: iconst_0
      16: iload_1
      17: invokestatic  #27                 // Method java/lang/System.arraycopy:(Ljava/lang/Object;ILjava/lang/Object;II)V
      20: aload_2
      21: areturn

  public static Foo valueOf(java.lang.String);
    Code:
       0: ldc           #1                  // class Foo
       2: aload_0
       3: invokestatic  #35                 // Method java/lang/Enum.valueOf:(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum;
       6: checkcast     #1                  // class Foo
       9: areturn
}

Мой вопрос: что физически отличается между перечислимым, скомпилированным с помощью javac, и перечислением, скомпилированным с помощью Eclipse, которое заставляет javap не показывать аргументы конструктора для перечисляемого с помощью javac перечисления? И является ли эта разница ошибкой (в javap, в javac или Eclipse)?

1 ответ

Решение

Параметры и тип возврата метода внутри файла класса описываются дескриптором метода.

С введением дженериков в 1.5. дополнительная информация была введена в формат файла класса, подпись метода.

"Дескриптор метода" используется для описания метода после стирания типа, "сигнатура метода" дополнительно содержит информацию об общем типе.

Сейчас javap распечатывает подпись метода (которая содержит больше информации), и когда -v флаг установлен, он также печатает дескриптор.

Это показывает, что также конструктор javac сгенерированный класс перечисления имеет дескриптор метода с типами параметров String а также int, Теперь также ясно, почему сгенерированный код Elipse и javac работают. Оба вызывают приватный конструктор с аргументами String а также int,

Что еще нужно объяснить: почему javac создать подпись, которая вообще отличается от дескриптора - никаких дженериков не задействовано?

Во всяком случае, поведение javac относительно конструктора enum вызвал другие проблемы и отчет об ошибке для javac было подано:

Для конструктора объявления перечисления нет необходимости иметь атрибут Signature, хранящий сигнатуру метода, если 1) конструктор не является универсальным и 2) его формальные типы параметров не являются ни параметризованными типами, ни переменными типов. Это ошибка, если javac ожидает атрибут Signature для конструктора, написанного выше.

Следующие комментарии и классификация случая показывают, что это реальная ошибка в javac,

Другие вопросы по тегам