意外的types安全违规

在以下代码中,将dowcast转换为表面上不兼容的types将通过编译:

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

ItemList<Item>是完全不同的types,因此投射不会成功。 为什么编译器允许这个?

一个List<Item>很可能是一个Item。 看例如:

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

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

在运行时,方法返回的实际对象的types将被检查,如果它实际上不是一个Itemtypes的对象,将会得到一个ClassCastExceptionexception。

相关的规格条目可以在这里find 。 设S为源,T为目标; 在这种情况下,源是接口,目标是非最终types。

如果S是一个接口types:

  • 如果T是一个数组types,那么S必须是java.io.SerializableCloneable (由数组实现的唯一接口)types,否则会发生编译时错误。

  • 如果T是不是最终的types(§8.1.1),那么如果存在T的超typesX和S的超typesY,使得X和Y都是可certificate的不同的参数化types,并且删除X和Y是一样的,会发生编译时错误。

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

我们花了几个读数来获得这个结果,但是让我们从头开始吧。

  • 目标不是一个数组,所以这个规则不适用。
  • 我们的目标没有与之相关的参数化types,所以这个规则不适用。
  • 这意味着在编译的时候,转换总是合法的,因为JB Nizet所说明的原因:我们的目标类可能不会实现源,而是一个子类。

这也意味着,如果我们投射到一个没有实现接口的最终类​​,它将无法工作:

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