我曾认为java擦除会在编译时清除泛型类型,但是当我自己测试它时,我意识到字节码中有一些关于泛型类型的信息。
这是我的测试:
我写了两个类:
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>
显然,我在字节码中有通用信息,那么这个擦除是什么??
发布于 2013-07-03 12:21:41
一些泛型类型信息存储在Signature
属性中。请参阅JLS 4.8和4.6和JVM spec 4.3.4。阅读here
关于Java语言中泛型最常见的抱怨可能是它们没有被具体化--没有办法在运行时知道
List<String>
与List<Long>
有什么不同。我已经习惯了这一点,以至于我很惊讶地遇到了Neil Gafter关于超级类型令牌的工作。事实证明,虽然JVM不会跟踪泛型类实例的实际类型参数,但它会跟踪泛型类的子类的实际类型参数。换句话说,虽然新的ArrayList<String>()
在运行时实际上只是一个新的ArrayList()
,但如果一个类扩展了ArrayList<String>
,那么JVM就知道String
是List
的类型参数的实际类型参数。
发布于 2013-07-03 12:57:18
这个擦除是什么东西??
擦除是从泛型类型到原始类型的映射。常见的短语“由于擦除”本质上是没有意义的。重要的是使用映射的规范。
这里有两个有趣的用法。
add(List<String>)
和add(List<Integer>)
。重载可能不是一个好主意,也没有很大的意愿去添加这个特性。(String)
,但如果您强制转换为List<String>
,则只会检查该类型(List
)的擦除。您可以让List<String>
和List<Integer>
类型的变量指向完全相同的实例。实际上,您不应该在1.5和更高版本中使用(引用类型的)类型转换。在可行的情况下,泛型信息保存在类文件中,并通过反射提供。所以你可以在类定义、超类型、字段、方法、构造函数等方面找到它。
发布于 2013-07-04 17:47:20
这是术语的准确使用实际上很重要的一个实例:字节码是Java虚拟机的指令集。类文件不仅包含字节码,还包含用于链接的信息(字段签名、方法签名等),用于字节码验证器、调试器等。
类型擦除意味着泛型类型信息不会转换为字节码;更具体地说,泛型类型的所有实例在字节码中共享相同的表示。同样,运行库跟踪的对象的动态类型(由cast和instanceof运算符使用,并可通过getClass()获得)对于泛型类的所有实例都是相同的,而与源代码中提供的任何类型参数无关。
您的实验证明,泛型类型信息保留在类文件中,更具体地说,保留在方法和字段签名的类型中。这并不奇怪,因为签名实际上是在编译时使用的。也可以在链接时使用,甚至可以通过反射api访问。关键的区别在于它们是字段或方法的声明类型,而不是实际对象的运行时类型。
也就是说,从Java1.5开始,我们必须区分变量的声明类型和它引用的对象的运行时类型。前者支持泛型,后者不支持。是的,这意味着编译时和运行时类型之间没有一对一的对应关系。
https://stackoverflow.com/questions/17447781
复制