Java泛型实际上是由type erasure实现的,所以字节码中没有类型信息。
例如,让我们看一下两个类,它们声明了一个List
字段,一个是泛型的,另一个是非泛型的:
class NonGeneric {
List list;
}
和,
class Generic {
List<String> list;
}
在这两种情况下,结果字节码如下:
Code:
Stack=3, Locals=1, Args_size=1
0: aload_0
1: invokespecial #1; //Method java/lang/Object."<init>":()V
4: aload_0
5: new #2; //class java/util/ArrayList
8: dup
9: invokespecial #3; //Method java/util/ArrayList."<init>":()V
12: putfield #4; //Field list:Ljava/util/List;
15: return
没有对ArrayList
或List
中使用的String
类型的引用。因此,我们可以看到泛型确实是通过类型擦除来实现的。
然而,如果我们看一下常量池,我们可以找到一个差异。
非泛型常量池:
Constant pool:
const #1 = Method #6.#15; // java/lang/Object."<init>":()V
const #2 = class #16; // java/util/ArrayList
const #3 = Method #2.#15; // java/util/ArrayList."<init>":()V
const #4 = Field #5.#17; // NonGeneric.list:Ljava/util/List;
const #5 = class #18; // NonGeneric
const #6 = class #19; // java/lang/Object
const #7 = Asciz list;
const #8 = Asciz Ljava/util/List;;
const #9 = Asciz <init>;
const #10 = Asciz ()V;
// snip the rest //
通用常量池:
Constant pool:
const #1 = Method #6.#17; // java/lang/Object."<init>":()V
const #2 = class #18; // java/util/ArrayList
const #3 = Method #2.#17; // java/util/ArrayList."<init>":()V
const #4 = Field #5.#19; // Generic.list:Ljava/util/List;
const #5 = class #20; // Generic
const #6 = class #21; // java/lang/Object
const #7 = Asciz list;
const #8 = Asciz Ljava/util/List;;
const #9 = Asciz Signature;
const #10 = Asciz Ljava/util/List<Ljava/lang/String;>;;
const #11 = Asciz <init>;
const #12 = Asciz ()V;
// snip the rest//
可以看到,在Generic
类中,我们可以看到常量池中有两个额外的常量,#9
和#10
,这表明List
具有String
的泛型类型。
(并结合我从Chris Jester-Young's answer学到的新知识)
进一步查看类文件的反汇编,在Generic
类的Code: block
之前有一个对常量#10的引用:
java.util.List list;
Signature: length = 0x2
00 0A
十六进制值0A
是以十进制表示的10
,它引用常量池#10
const #10 = Asciz Ljava/util/List<Ljava/lang/String;>;;
因此,使用常量池中的信息来指示字段是泛型类型。