意外的类型安全违规

Unexpected type safety violation

在下面的代码中,对明显不兼容类型的向下转换通过了编译:

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

ItemList<Item> 是不同的类型,因此转换永远不会成功。为什么编译器允许这样做?

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

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

强制转换告诉编译器:"I know you can't be sure that this is a object of type Item, but I know better than you, so please compile"。如果返回的对象不可能是 Item 的实例(例如,Integer 永远不可能是 String),编译器只会拒绝编译它

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

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

If S is an interface type:

  • If T is an array type, then S must be the type java.io.Serializable or Cloneable (the only interfaces implemented by arrays), or a compile-time error occurs.

  • If T is a type that is not final (§8.1.1), then if there exists a supertype X of T, and a supertype Y of S, such that both X and Y are provably distinct parameterized types, and that the erasures of X and Y are the same, a compile-time error occurs.

    Otherwise, the cast is always legal at compile time (because even if T does not implement S, a subclass of T might).

读了几遍才明白这一点,但让我们从头开始。

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

这也意味着如果我们强制转换为未实现该接口的最终 class,它将无法工作,根据此代码段:

If S is not a parameterized type or a raw type, then T must implement S, or a compile-time error occurs.