12

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

这是我的测试:

我写了 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>

所以很明显我在字节码中有通用信息那么这个擦除是什么?

4

5 回答 5

6

这个擦除的东西是什么?

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

有两个有趣的用途。

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

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

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

于 2013-07-03T12:57:18.090 回答
5

一些通用类型信息存储在Signature属性中。请参阅JLS 4.84.6以及JVM 规范 4.3.4在这里阅读:

可能对 Java 中的泛型最常见的抱怨是它们没有被具体化——没有办法在运行时知道 a 与 aList<String>有什么不同List<Long>。我已经习惯了这一点,以至于看到 Neil Gafter 关于 Super Type Tokens 的作品时我感到非常惊讶。事实证明,虽然 JVM 不会跟踪泛型类实例的实际类型参数,但它会跟踪泛型类子类的实际类型参数。换句话说,虽然ArrayList<String>()new 在运行时实际上只是一个 new ArrayList(),但如果一个类 extends ArrayList<String>,那么 JVM 就知道这String是 的类型参数的实际类型List参数。

Neal Gafter 的博客

于 2013-07-03T12:21:40.507 回答
3

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

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

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

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

于 2013-07-04T17:47:19.780 回答
1

类型信息将从此处删除

 integerList = new ArrayList<Integer>(); 

在字节码中它相当于

integerList = new ArrayList(); 

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

于 2013-07-03T12:48:45.023 回答
0

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

您看到的签名仅用于表明该字段是通用的。

于 2013-07-03T12:30:08.033 回答