为什么一个List<SuperClass>对象可以转换为一个Subclass对象?

4

我发现,情况1和情况3可以编译通过,但情况2无法通过编译。(SubClassB扩展了一个抽象类SuperClassA) 我想知道的是,为什么情况1和情况3没有编译错误。 如果这是JDK的bug,为什么情况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;
2个回答

6

在不安全的类型转换中,编译器无法确定该转换是否应该被允许,因此会产生警告。(在旧版本的Java中可以无警告地进行转换,所以现在很难删除)

注意:Long是一个final类,并且它假设不存在任何子类能够使得这个类型转换可行。

您可以创建一个类,如下所示:

class MyException extends RuntimeException implements List<Exception> {

所以这些都是有效的代码行。
List<Exception> e = new MyException();
RuntimeException d = (RuntimeException) e; // ok.

谢谢你的帮助,很酷。 (我已经更正了我的问题词,它是编译错误而不是异常) - Ophiuchus Hahn

3
请注意,情况1和情况3在运行时都会失败。
在编译时,只有当语言规范要求时,编译器才会抱怨不兼容的转换。语言规范允许情况1和情况3,因为它们不会100%失败。
就编译器而言,情况1不会100%失败,因为它认为a可以包含实现List的任何类型的实例。如果SubclassB的子类实际上实现了List,并且a实际上包含该SubclassB的子类的实例,那么转换将成功!
对于情况2也是同样的道理。如果RuntimeException的子类实际上实现了List,并且e实际上包含该RuntimeException的子类的实例,那么转换将成功!
案例2显示了一个错误,因为Longfinal。没有可能有一个实现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的子类可能实现了S)。 - 如果T是final类类型,则: - 如果S不是参数化类型或原始类型,则T必须实现S,否则会出现编译时错误。

编译器只会在它百分之百确定转换将失败时,才会抱怨不兼容的转换。但 (Integer) (String) null 不仅无法通过编译,也不能成功转换(如 (Integer) (Object) (String) null 所示)。 - Andy Turner

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接