编译器是否在编译时删除了泛型

问题描述 投票:9回答:4

在本教程中,reflection指出:

[...]因为泛型是通过类型擦除实现的,它在编译期间删除了有关泛型类型的所有信息

我的知识是使用泛型,以便在编译时编译器可以检查类型安全性。即快速接近失败。但链接提到类型擦除会在编译期间删除通用信息。

java generics
4个回答
11
投票

您引用的语句是正确的:编译器在编译过程中在内部使用泛型类型信息,在处理源时生成与类型相关的错误。然后,一旦验证完成,编译器就会生成类型擦除的字节代码,所有对泛型类型的引用都将替换为它们各自的类型擦除。

当您通过反射查看类型时,这一事实变得明显:所有接口,类和函数都变为非泛型,所有类型都绑定到泛型类型参数,并根据源中指定的泛型类型约束替换为非泛型类型码。虽然反射API确实具有在运行时访问与泛型*相关的一些信息的规定,但是当您通过反射访问类时,虚拟机无法检查完全兼容的通用类型。

例如,如果你创建一个类型为List<String>的类成员并尝试将List<Integer>设置为它,编译器就会抱怨。但是,如果你试图通过反射来做同样的事情,编译器就不会发现,并且代码将在运行时以与没有泛型的方式相同的方式失败:

class Test {
    private List<String> myList;
    public void setList(List<String> list) {
        myList = list;
    }
    public void showLengths() {
        for (String s : myList) {
             System.out.println(s.length());
        }
    }
}

...

List<Integer> doesNotWork = new ArrayList<Integer>();
doesNotWork.add(1);
doesNotWork.add(2);
doesNotWork.add(3);
Test tst = new Test();
tst.setList(doesNotWork); // <<== Will not compile
Method setList = Test.class.getMethod("setList", List.class);
setList.invoke(tst, doesNotWork); // <<== This will work;
tst.showLengths(); // <<== However, this will produce a class cast exception

Demo on ideone.


*有关在运行时获取与泛型类型相关的信息的详细信息,请参阅this answer


4
投票

一些泛型保留在编译的类中 - 例如,特别包括方法签名和类定义。在运行时,没有对象保留其完整的泛型类型,但即使在运行时,您也可以查找类或方法的泛型定义。

例如,如果你有

class Foo {
  List<String> getList() { ... }

  public static void main(String[] args) {
    System.out.println(Foo.class.getMethod("getList").getGenericReturnType());
    // prints "List<String>"
    List<String> list = new Foo().getList();
    // there is no way to get the "String" parameter on list
}

1
投票

这意味着,当它被转换为字节码时。为了检查是否使用了正确的类型,使用了泛型。但在生成字节码时,信息被删除


0
投票

在java中,Generics只是一个占位符。 Java Run Time对泛型没有任何线索。这都是编译时间的伎俩。

当您在类中声明字段属性时,设置泛型与场景完全相似

  • 对象(当你只声明类型为T时)
  • MyObject(当你声明类型为T extends MyObject时)。

编译完成后,所有内容都将根据类型进行连线。这就是所谓的Type Erasure。

© www.soinside.com 2019 - 2024. All rights reserved.