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

Java 编译器是否在编译时删除泛型,java,generics,Java,Generics,在本教程中,它指出: […]因为泛型是通过类型擦除来实现的,类型擦除在编译过程中会删除所有关于泛型类型的信息 据我所知,泛型的使用是为了在编译时编译器可以检查类型安全性。i、 e快速失效方法。 但是链接提到类型擦除会在编译过程中删除泛型信息。它的意思是,当它被转换成字节码时。为了检查是否使用了正确的类型,使用了泛型。但是在字节码生成时,信息会被删除。您引用的语句是正确的:编译器在编译过程中在内部使用泛型类型信息,在处理源代码时生成与类型相关的错误。然后,验证完成后,编译器生成类型擦除字节码,所有

在本教程中,它指出:

[…]因为泛型是通过类型擦除来实现的,类型擦除在编译过程中会删除所有关于泛型类型的信息

据我所知,泛型的使用是为了在编译时编译器可以检查类型安全性。i、 e快速失效方法。
但是链接提到类型擦除会在编译过程中删除泛型信息。

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

当您通过反射查看类型时,这一事实变得很明显:所有接口、类和函数都变为非泛型,所有绑定到泛型类型参数的类型都将替换为基于源代码中指定的泛型类型约束的非泛型类型。尽管反射API确实提供了在运行时访问一些与泛型相关的信息*,但当您通过反射访问类时,虚拟机无法检查确切的泛型类型的兼容性

例如,如果您将一个类成员设置为
List
类型,并尝试在其中设置
List
,编译器就会抱怨。但是,如果您尝试通过反射执行相同的操作,编译器将无法发现,并且代码将在运行时以与没有泛型时相同的方式失败:

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
类测试{
私人名单;
公共无效集合列表(列表){
myList=列表;
}
公众假期{
for(字符串s:myList){
System.out.println(s.length());
}
}
}
...
List doesNotWork=new ArrayList();
不工作。添加(1);
不工作。添加(2);
不工作。添加(3);
测试tst=新测试();

tst.集合列表(不工作);// 一些泛型保留在编译类中——例如,具体包括方法签名和类定义。在运行时,没有对象保持其完整的泛型类型,但即使在运行时,您也可以查找类或方法的泛型定义

例如,如果你有

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
}
class-Foo{
List getList(){…}
公共静态void main(字符串[]args){
System.out.println(Foo.class.getMethod(“getList”).getGenericReturnType());
//打印“列表”
List List=new Foo().getList();
//无法获取列表中的“String”参数
}

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

设置泛型与将类中的字段属性声明为

  • 对象(仅将类型声明为
    T
  • MyObject(当您将类型声明为
    T时,扩展MyObject

编译之后,所有的信息都会根据类型进行连接。这就是编译期间但经过验证后被称为类型擦除的内容。显然,在编译器使用这些信息之前,不会删除这些信息。编译器不会从
.java
文件中删除任何内容,而是会创建一个e> .class
文件,它不会复制注释、导入或所有通用信息(某些信息仍然存在)由于您从零开始,因此无法删除它们。泛型类型替换为
java.lang.Object
,它们各自的类型擦除。@RohitJain您是对的,它并不总是
java.lang.Object
-谢谢!这就是它被称为“擦除类型”的原因-1;此解释具有误导性。您可以通过使用反射返回一些泛型类型约束。例如,如果调用
Test.class.getDeclaredField(“myList”).getGenericType()
,您将获得
列表
,而不是
列表
。@LouisWasserman感谢您的评论。请查看编辑,如果您认为编辑后的解释仍有误导性,请告诉我。谢谢!