意外的类型安全违规

时间:2015-01-02 19:35:06

标签: java casting

在下面的代码中,对于显然不兼容的类型的dowcast传递编译:

public class Item {
  List<Item> items() { return asList(new Item()); }
  Item m = (Item) items();
}

ItemList<Item>是完全不同的类型,因此演员阵容永远不会成功。为什么编译器允许这样做?

2 个答案:

答案 0 :(得分:52)

List<Item>很可能是一个项目。例如见:

public class Foo extends Item implements List<Item> {
    // implement required methods
}

演员告诉编译器:“我知道你不能确定这是Item类型的对象,但我比你知道的更好,所以请编译”。如果返回的对象不可能是Item的实例(例如,Integer不能是String

,编译器将只拒绝编译。

在运行时,将检查方法返回的实际对象的类型,如果它实际上不是Item类型的对象,则会得到ClassCastException。

答案 1 :(得分:17)

相关规范条目can be found here。设S为源,T为目标;在这种情况下,源是接口,目标是非最终类型。

  

如果S是接口类型:

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

  •   
  • 如果T是非final的类型(§8.1.1),那么如果存在T的超类型X和S的超类型Y,那么X和Y都是   可证明不同的参数化类型,以及X和X的擦除   Y是相同的,发生编译时错误。

         

    否则,演员在编译时总是合法的(因为即使   T没有实现S,T的子类可能)。

  •   

这需要一些读数来直截了当,但让我们从顶部开始。

  • 目标不是数组,因此该规则不适用。
  • 我们的目标没有与之关联的参数化类型,因此该规则不适用。
  • 这意味着演员在编译时总是合法的,由于JB Nizet所说明的原因:我们的目标类可能没有实现源,但子类可能

这也意味着如果我们按照这个片段转换到没有实现接口的最终类​​,它将无法工作:

  

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