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

时间:2013-10-08 16:23:38

标签: java generics

reflection的本教程中,它指出:

  

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

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

4 个答案:

答案 0 :(得分: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

答案 1 :(得分: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
}

答案 2 :(得分:1)

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

答案 3 :(得分:0)

在Java中,泛型只是一个占位符。 Java运行时没有有关泛型的任何线索。这都是编译时的把戏。

在您将类中的字段属性声明为

时,设置泛型与场景完全相似。
  • 对象(当您将类型声明为T时)
  • MyObject(当您将类型声明为T extends MyObject时。)

编译后,将根据类型进行连接。这就是类型擦除

相关问题