在reflection的本教程中,它指出:
[...]因为泛型是通过类型擦除实现的,它在编译期间删除了有关泛型类型的所有信息
我的知识是使用泛型,以便在编译时编译器可以检查类型安全性。即快速接近失败。 但链接提到类型擦除会在编译期间删除通用信息。
答案 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
* 有关在运行时获取与泛型类型相关的信息的详细信息,请参阅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
时)T extends MyObject
时。)编译后,将根据类型进行连接。这就是类型擦除。