Boa*_*ann 7 java enums javap .class-file ecj
当我用javap反汇编枚举时,enum的隐式构造函数参数似乎缺失了,我无法弄清楚为什么.
这是一个枚举:
enum Foo { X }
Run Code Online (Sandbox Code Playgroud)
我用这个命令编译和反汇编(在Java 8u60上):
javac Foo.java && javap -c -p Foo
Run Code Online (Sandbox Code Playgroud)
这是我得到的输出:
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
}
Run Code Online (Sandbox Code Playgroud)
我的混淆是用于实例化每个枚举常量的私有构造函数.反汇编显示它不带参数(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
}
Run Code Online (Sandbox Code Playgroud)
我的问题是:什么身体是的javac编译枚举和一个Eclipse编译枚举导致的javap不显示使用javac编译枚举的构造函数的参数之间有什么不同?这是一个错误(在javap,javac或Eclipse中)?
类文件中方法的参数和返回类型由方法描述符描述。
随着 1.5 中泛型的引入。类文件格式中引入了附加信息,即方法签名。
“方法描述符”用于描述类型擦除后的方法,“方法签名”另外包含通用类型信息。
现在javap打印出方法签名(其中包含更多信息),并且当-v设置标志时,它还会打印描述符。
这表明生成的枚举类的构造函数也有一个带有参数类型和 的javac方法描述符。现在也很清楚为什么 Elipse 和 javac 生成的代码都能工作。两者都使用参数和调用私有构造函数。StringintStringint
还需要解释的是:为什么要javac创建一个与描述符完全不同的签名——不涉及泛型?
javac无论如何,有关枚举构造函数 的行为引起了其他麻烦javac,并提交了错误报告:
如果 1) 构造函数不是泛型,并且 2) 其形式参数类型既不是参数化类型也不是类型变量,则枚举声明的构造函数不需要具有存储方法签名的 Signature 属性。如果 javac 需要上面编写的构造函数的 Signature 属性,则这是一个错误。
以下评论和案例的分类表明这是 中的一个真正的错误javac。
| 归档时间: |
|
| 查看次数: |
586 次 |
| 最近记录: |