意外的types安全违规
在以下代码中,将dowcast转换为表面上不兼容的types将通过编译:
public class Item { List<Item> items() { return asList(new Item()); } Item m = (Item) items(); }
Item
和List<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.Serializable
或Cloneable
(由数组实现的唯一接口)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,否则会发生编译时错误。