泛型类型存储在 java 类文件中的什么位置?
我很清楚,泛型类型在编译时会从Java代码中删除。1.5+ JVM 使用哪些信息(属性?)来实现 等?getGenericType
我很清楚,泛型类型在编译时会从Java代码中删除。1.5+ JVM 使用哪些信息(属性?)来实现 等?getGenericType
它们存储在属性中;请参阅更新后的 Java 虚拟机规范的第 4.8.8 节,以及有关字段类型签名的格式的第 4.4.4 节。Signature
下面是一个使用的示例:javap -verbose java.util.Map
public interface java.util.Map
SourceFile: "Map.java"
Signature: length = 0x2
00 1E
[other attributes omitted]
此处的属性指定(如果您将其读作大端量,就像 JVM 类文件格式中的所有整数量一样)常量池值 #30 (30 = 0x1E)。所以让我们来看看那里:Signature
const #30 = Asciz <K:Ljava/lang/Object;V:Ljava/lang/Object;>Ljava/lang/Object;;
在 4.4.4 中指定的语法上下文中阅读本文。因此,这使用两个类型参数和 。类型本身 () 也扩展了类,并且没有接口。K extends java.lang.Object
V extends java.lang.Object
Map
java.lang.Object
Java泛型确实是通过类型擦除实现的,因此字节码中没有类型信息。
例如,让我们看一下声明字段的两个类,一个是泛型的,另一个是非泛型的: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
没有对 nor 中使用的类型的引用。因此,我们可以看到泛型确实是通过类型擦除实现的。String
ArrayList
List
但是,如果我们看一下恒定池,我们可以找到差异。
非泛型常量池:
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的答案中学到的新知识)
进一步查看类文件的反汇编,在类的 前面有一个对常量 #10 的引用:Code: block
Generic
java.util.List list;
Signature: length = 0x2
00 0A
十六进制值是十进制的,它指的是常量池:0A
10
#10
const #10 = Asciz Ljava/util/List<Ljava/lang/String;>;;
因此,使用常量池中的信息来指示字段属于泛型类型。