Java类型擦除是否会擦除我的泛型类型?

Java类型擦除是否会擦除我的泛型类型?,java,generics,jvm,bytecode,type-erasure,Java,Generics,Jvm,Bytecode,Type Erasure,我认为java擦除会在编译时清除泛型类型,但是当我自己测试它时,我意识到字节码中有一些关于泛型类型的信息 这是我的测试: 我写了两门课: import java.util.*; public class Test { List integerList; } 及 import java.util.*; 公开课考试{ 列表整型列表; } 我编译了这两个类,在泛型类的某个地方我看到了这一行 integerList{blah blah}Ljava/util/List;{blah blah

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

这是我的测试:

我写了两门课:

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

import java.util.*;
公开课考试{
列表整型列表;
} 
我编译了这两个类,在泛型类的某个地方我看到了这一行

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}%Ljava/util/List;{废话废话}
在非泛型类中:

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

很明显,字节码中包含了泛型信息,所以擦除是什么呢?

一些泛型信息存储在
签名
属性中。参考和。阅读:

关于Java中泛型最常见的抱怨可能是它们没有具体化——在运行时没有办法知道
列表
列表
有什么不同。我已经习惯了这一点,以至于看到尼尔·加弗特(Neil Gafter)关于超级类型代币的工作,我感到非常惊讶结果表明,虽然JVM不会跟踪泛型类实例的实际类型参数,但它确实跟踪泛型类子类的实际类型参数。换句话说,新的
ArrayList()
在运行时实际上只是一个新的
ArrayList()
,如果类扩展了
ArrayList
,那么JVM知道
String
List
的类型参数的实际类型参数


和。

擦除意味着字节码中不包含泛型键入(当创建或使用列表时)


您看到的签名仅用于指示该字段是通用的。

类型信息将从此处删除

 integerList = new ArrayList<Integer>(); 
在运行时,从integerList对象不可能知道它的编译时类型

这是什么擦除的东西

擦除是从泛型到原始类型的映射。常见的短语“因为擦除”基本上没有意义。重要的是使用映射的规范

有两个有趣的用途

  • 它用于将方法签名从使用泛型映射到原始类型。它是用于重载的原始类型签名。这导致了“擦除”的绝大多数问题。例如,同一类型中不能有两种方法
    add(List)
    add(List)
    。重载可能不是一个好主意,也没有人愿意添加这个特性

  • 运行时对象实例可用的类型是使用“已擦除”创建的类型。因此,如果您强制转换到运行时将被检查的
    (字符串)
    ,但是如果您强制转换到
    列表
    ,则只会检查该类型(
    列表
    )的擦除。您可以让
    List
    List
    类型的变量指向完全相同的实例。实际上,您不应该在1.5及更高版本中使用(引用类型的)强制转换


在实际情况下,泛型信息保存在类文件中,并通过反射提供。因此,您可以在类定义、超类型、字段、方法、构造函数等方面找到它。

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

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

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


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

这是误导。元数据仍然只是用于反射。它没有具体化,当然!但是从“integerList”的定义,也就是List,JVM可以计算出类型是的,带有reflect from字段,但是如果你得到ArrayList的一个实例,就无法找到它的通用类型谢谢,你的回答对我帮助很大。
 integerList = new ArrayList<Integer>(); 
integerList = new ArrayList();