7

当我用 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读取传递的枚举常量名称和序号以及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 中)?

4

1 回答 1

3

类文件中方法的参数和返回类型由方法描述符描述

随着 1.5 中泛型的引入。附加信息被引入类文件格式,方法签名

“方法描述符”用于描述类型擦除后的方法,“方法签名”额外包含泛型类型信息。

现在javap打印出方法签名(其中包含更多信息),并且当设置-v标志时,它还打印描述符。

这表明javac生成的枚举类的构造函数也有一个带有参数类型String和的方法描述符int。现在也很清楚为什么 Elipse 和 javac 生成的代码都有效。String两者都使用参数和调用私有构造函数int

还需要解释什么:为什么要javac创建一个与描述符完全不同的签名——不涉及泛型?

无论如何,javac关于枚举构造函数的行为引起了 其他麻烦javac,并且提交了一个错误报告:

如果 1) 构造函数不是通用的并且 2) 它的形式参数类型既不是参数化类型也不是类型变量,则枚举声明的构造函数不需要具有存储方法签名的 Signature 属性。如果 javac 期望上面编写的构造函数具有 Signature 属性,这是一个错误。

以下评论和案例分类表明这是javac.

于 2015-09-28T19:32:19.780 回答