10

在本关于反射的教程中,它指出:

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

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

4

4 回答 4

12

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

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

例如,如果您创建一个类型的类成员List<String>并尝试将 a 设置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

ideone 上的演示。


*有关在运行时获取与泛型类型相关的信息的详细信息,请参阅此答案。

于 2013-10-08T16:26:59.583 回答
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
}
于 2013-10-08T16:32:46.997 回答
1

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

于 2013-10-08T16:26:53.263 回答
0

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

设置泛型与场景完全相同,当您将类中的字段属性声明为

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

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

于 2018-09-17T07:05:05.520 回答