为什么要编译具有这种泛型绑定的代码



在下一段代码中,我有一个参数化的方法,它创建一个ArrayList,将其转换为<T extends List<Integer>>。该列表被分配给类型为MyClass的变量

public class MyClass {
public static void main(String ... args) {
MyClass s = createList(); // compiler should find here an error
}
private static <T extends List<Integer>> T createList()
{
return (T) new ArrayList<Integer>();
}
}

我希望编译器不会处理该代码,并且会向我显示错误。但是该类被编译,并且在运行时,应用程序可以预测地抛出强制转换异常:

线程"main"中的异常 java.lang.ClassCastException: class java.util.ArrayList 不能强制转换为类 java.lang.String (java.util.ArrayList 和 java.lang.String 位于模块 java.base 中 加载器'bootstrap'(在pkgname。MyClass.main(MyClass.java:9(

如果我将泛型的签名更改为T extends ArrayList它按预期工作 - 显示编译错误:

错误:(9, 31( java:不兼容类型:推理变量 T 有 不兼容的上限 pkgname。MyClass,java.util.ArrayList

因此,这仅适用于接口。 如果我尝试将返回值分配给IntegerNumberString等类或MyClass最终值,那么我会收到警告

Though assignment is formal correct, it could lead to ClassCastException at runtime. Expected: 'String', actual: 'List<Integer> & String'

是的,强制转换显示警告Unchecked cast: 'java.unil.ArrayList' to 'T'但是如果我使用接口作为泛型的参数,为什么编译器无法检测到错误的断言?

我怀疑当我使用接口作为泛型的绑定时,编译器可能会假设即使MyClass还没有继承接口,MyClass 的后代之一也可能继承,因此代码可能是正确的。在类final时购买 - 绝对不可能将返回值分配给MyClass

可能存在一个既extends List<Integer>又扩展MyClassT(例如class MyListClass extends MyClass implements List<Integer>

编译器不会查看createList方法来弄清楚它返回的内容,它只是相信它执行原型描述的操作。

由于在方法主体中使用了未经检查的强制转换,因此可以通过这种方式中断类型系统。这就是为什么你会收到关于未经检查的演员表的警告。

实际上,未选中的强制转换意味着您在该行禁用类型检查,因此可以实现您实际上未满足的返回类型(您实际上并没有如上所述返回MyListClass,您只是声称您这样做(。

这不适用于非接口类型的原因是类型系统不允许同时使用两个不相关的非接口类型的类型(一个事物不能是同一类型的StringMyClass(。

最新更新