我发现,情况1和情况3可以没有错误地进行编译,但是可以是情况2。(SubClassB扩展了SuperClassA,它是抽象类) 我想知道的是,为什么情况1和情况3没有编译错误。 如果是JDK错误,为什么情况2无法通过强制转换检查?
// case 1
List<SuperClassA> a = new ArrayList<>();
SubClassB b = (SubClassB) a;
// case 2
List<Number> m = new ArrayList<>();
Long n = (Long) m; //Error:(xx,yy) java: incompatible types: java.util.List<java.lang.Number> cannot be converted to java.lang.Long
// case 3
List<Exception> e = new ArrayList<>();
RuntimeException d = (RuntimeException) e;
答案 0 :(得分:5)
在不安全的强制转换的情况下,您会得到警告,因为编译器无法确定是否应允许此强制转换。 (在较早的Java版本中允许在没有警告的情况下使用,因此现在很难删除)
注意:Long
是final
的类,它假定没有强制转换可能起作用的Long
子类。
您可以创建类似的类
class MyException extends RuntimeException implements List<Exception> {
所以这些是有效的代码行
List<Exception> e = new MyException();
RuntimeException d = (RuntimeException) e; // ok.
答案 1 :(得分:2)
请注意,情况1和情况3都将在运行时失败。
在编译时,仅当语言规范如此说明时,编译器才会抱怨不兼容的强制类型转换。语言规范允许情况1和3,因为它们不会100%失败。
就编译器而言,案例1不会100%失败,因为案例1认为a
可以包含实现List<SuperClassA>
的任何类型的实例。如果有一个SubclassB
的子类实际上实现了List<SuperClassA>
怎么办?如果a
实际上包含SubclassB
子类的实例怎么办?然后,投射将成功!
情况2也是如此。如果有一个RuntimeException
的子类实际实现了List<Exception>
,该怎么办?如果e
实际上包含RuntimeException
子类的实例怎么办?然后,投射将成功!
案例2显示错误,因为Long
是final
。 不能是实现List<Number>
的子类,所以它肯定会失败。
这是在规范的§5.5.1中指定的(斜体是相关位):
考虑到编译时引用类型S(源)和编译时引用类型T(目标),如果由于以下规则而没有发生编译时错误,则存在从S到T的转换转换。
...
如果S是接口类型:
如果T为数组类型,则S必须为
java.io.Serializable
或Cloneable
类型(唯一实现的接口 按数组),否则会发生编译时错误。如果T是不是
final
(第8.1.1节)的类或接口类型,则如果存在T的超类型X和S的超类型Y,则 X和Y都是可证明不同的参数化类型,并且 X和Y的擦除相同,则发生编译时错误。否则,强制转换在编译时始终合法(因为即使T 不实现S,则可能是T的子类。
如果T是最终的类类型,则:
–如果S不是参数化类型或原始类型,则T必须实现S,否则会发生编译时错误。