我很清楚,编译java代码时,泛型类型会从代码中删除。1.5+JVM使用什么信息(属性?)来实现 getGenericType 等等?
getGenericType
mutmk8jj1#
java泛型实际上是通过类型擦除实现的,因此字节码中没有类型信息。例如,让我们看两个类,它们声明 List 字段,一个为泛型,另一个为非泛型:
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
没有提到 String 中使用的类型 ArrayList 也不是 List . 所以,我们可以看到泛型确实是通过类型擦除实现的。但是,如果我们看一下常量池,我们可以发现一个不同。非泛型常量池:
String
ArrayList
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 .(结合了我从克里斯·杰斯特·杨的回答中学到的新知识)进一步查看类文件的反汇编,在 Code: block 的 Generic 班级:
Generic
#9
#10
Code: block
java.util.List list; Signature: length = 0x2 00 0A
十六进制值 0A 是 10 十进制表示常量池 #10 :
0A
10
const #10 = Asciz Ljava/util/List<Ljava/lang/String;>;;
因此,使用常量池中的信息来指示字段是泛型类型。
piwo6bdm2#
它们储存在 Signature 属性;请参阅更新的java虚拟机规范的第4.8.8节,以及字段类型签名的格式的第4.4.4节。下面是一个使用 javap -verbose java.util.Map :
Signature
javap -verbose java.util.Map
public interface java.util.Map SourceFile: "Map.java" Signature: length = 0x2 00 1E [other attributes omitted]
这个 Signature 属性在这里指定(如果您将其理解为big endian,就像jvm类文件格式中的所有整数一样)常量池值#30(30=0x1e)。让我们看一下:
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 ,没有接口。
K extends java.lang.Object
V extends java.lang.Object
Map
java.lang.Object
2条答案
按热度按时间mutmk8jj1#
java泛型实际上是通过类型擦除实现的,因此字节码中没有类型信息。
例如,让我们看两个类,它们声明
List
字段,一个为泛型,另一个为非泛型:而且,
在这两种情况下,产生的字节码如下:
没有提到
String
中使用的类型ArrayList
也不是List
. 所以,我们可以看到泛型确实是通过类型擦除实现的。但是,如果我们看一下常量池,我们可以发现一个不同。
非泛型常量池:
通用常量池:
可以看出,在
Generic
同学们,我们可以看到有两个额外的常数,#9
以及#10
,在常量池中,其中提到List
具有泛型类型String
.(结合了我从克里斯·杰斯特·杨的回答中学到的新知识)
进一步查看类文件的反汇编,在
Code: block
的Generic
班级:十六进制值
0A
是10
十进制表示常量池#10
:因此,使用常量池中的信息来指示字段是泛型类型。
piwo6bdm2#
它们储存在
Signature
属性;请参阅更新的java虚拟机规范的第4.8.8节,以及字段类型签名的格式的第4.4.4节。下面是一个使用
javap -verbose java.util.Map
:这个
Signature
属性在这里指定(如果您将其理解为big endian,就像jvm类文件格式中的所有整数一样)常量池值#30(30=0x1e)。让我们看一下:在4.4.4中指定的语法上下文中阅读。所以,这使用了两个类型参数,
K extends java.lang.Object
以及V extends java.lang.Object
. 类型本身(Map
)还扩展了类java.lang.Object
,没有接口。