为什么List <superclass>对象可以强制转换为子类对象?

时间:2019-03-21 18:47:01

标签: java compiler-errors

我发现,情况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;

2 个答案:

答案 0 :(得分:5)

在不安全的强制转换的情况下,您会得到警告,因为编译器无法确定是否应允许此强制转换。 (在较早的Java版本中允许在没有警告的情况下使用,因此现在很难删除)

注意:Longfinal的类,它假定没有强制转换可能起作用的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显示错误,因为Longfinal不能是实现List<Number>的子类,所以它肯定会失败。

这是在规范的§5.5.1中指定的(斜体是相关位):

  

考虑到编译时引用类型S(源)和编译时引用类型T(目标),如果由于以下规则而没有发生编译时错误,则存在从S到T的转换转换。

     

...

     

如果S是接口类型:

     
      
  • 如果T为数组类型,则S必须为java.io.SerializableCloneable类型(唯一实现的接口   按数组),否则会发生编译时错误。

  •   
  • 如果T是不是final(第8.1.1节)的类或接口类型,则如果存在T的超类型X和S的超类型Y,则   X和Y都是可证明不同的参数化类型,并且   X和Y的擦除相同,则发生编译时错误。

  •   
     

否则,强制转换在编译时始终合法(因为即使T   不实现S,则可能是T的子类。

     
      
  • 如果T是最终的类类型,则:

         

    –如果S不是参数化类型或原始类型,则T必须实现S,否则会发生编译时错误。

  •