Java类型擦除擦除我的泛型类型?

我认为java擦除会在编译时擦除泛型类型,但是当我自己测试它时,我意识到在Bytecode中有一些关于泛型类型的信息 .

这是我的测试:

我写了2节课:

import java.util.*;
public class Test {
    List integerList;
}

import java.util.*;
public class Test {
    List<Integer> integerList;
}

我编译了这两个类和泛型类中的某个地方,我看到了这一行

integerList{blah blah}Ljava/util/List;{blah blah}
Signature{blah blah}%Ljava/util/List<Ljava/lang/Integer;>;{blah blah}<init>

在非泛型类中:

integerList{blah blah}Ljava/util/List;{blah blah}<init>

所以显然我在字节码里面有通用信息所以这个擦除的东西是什么?

回答(5)

2 years ago

某些通用类型信息存储在 Signature 属性中 . 请参阅JLS 4.84.6JVM spec 4.3.4 . 阅读here

关于Java中泛型的最常见抱怨可能是它们没有被统一 - 在运行时没有办法知道List <String>与List <Long>有什么不同 . 我已经习惯了这一点,我很惊讶地遇到了Neil Gafter在Super Type Tokens上的工作 . 事实证明,虽然JVM不会跟踪泛型类实例的实际类型参数,但它会跟踪泛型类的子类的实际类型参数 . 换句话说,虽然新的ArrayList <String>()实际上只是运行时的新ArrayList(),但是如果类扩展了ArrayList <String>,那么JVM知道String是List的类型参数的实际类型参数 .

Neal Gafter's blog .

2 years ago

这个擦除的东西是什么?

Erasure 是从泛型到原始类型的映射 . 常用短语"because of erasure"基本上没有意义 . 重要的是使用映射的规范 .

有两个有趣的用途 .

  • 它's used to map method signatures from using generics to raw types. It is the raw-type signatures that used for overloading. This causes the vast majority of the problems with 2934161 . For instance, you can' t有两种方法 add(List<String>)add(List<Integer>) 在同一类型 . 重载可能不是很好的添加此功能的意愿 .

  • 运行时对象实例可用的类型是使用已擦除创建的类型 . 因此,如果转换为 (String) ,将在运行时检查,但如果转换为 List<String> ,则仅检查该类型的擦除( List ) . 您可以将 List<String>List<Integer> 类型的变量指向完全相同的实例 . 实际上,您不应该在1.5及更高版本中使用强制转换(引用类型) .

在可行的情况下,通用信息保存在类文件中并通过反射提供 . 所以你会在类定义,超类型,字段,方法,构造函数等上找到它 .

2 years ago

这是准确使用术语实际上很重要的一个例子:字节码是Java虚拟机的指令集 . 类文件包含字节码,但也包含用于链接的信息(字段签名,方法签名,...),字节码验证器,调试器,...

类型擦除意味着泛型类型信息不会转换为字节代码;更具体地说,泛型类型的所有实例在字节代码中共享相同的表示 . 同样,运行时跟踪的对象的动态类型(由cast和instanceof运算符使用,并且可通过getClass()获得)对于泛型类的所有实例都是相同的,无论源中提供的任何类型参数如何码 .

您的实验证明泛型类型信息保留在类文件中,更具体地说,保留在方法类型和字段签名中 . 这并不奇怪,因为签名实际上是在编译时使用的 . 也可以在链接时使用,甚至可以通过反射api访问 . 关键的区别在于它们是声明的字段或方法类型,而不是实际对象的运行时类型 .

也就是说,从Java 1.5开始,我们必须区分变量的声明类型和它引用的对象的运行时类型 . 前者支持泛型,后者则不支持 . 是的,这意味着编译时和运行时类型之间没有一对一的对应关系 .

2 years ago

类型信息将从此处删除

integerList = new ArrayList<Integer>();

在字节码中它将等同于

integerList = new ArrayList();

并且没有机会在运行时知道integerList对象的编译时间类型是什么 .

2 years ago

擦除意味着通用类型不包含在字节代码中(当创建或使用列表时) .

您看到的签名仅用于表示该字段是通用的 .