首页 > 解决方案 > 为什么这段代码在编译时没有发出 `ClassCastException` 警告?

问题描述

我已经看到了这个问题,但它似乎没有回答下面的两种情况,也没有参考任何进一步的文档,我很想阅读。

我有以下代码片段:

    public static void main(String[] args) {
        // compile-time OK
        // run-time ClassCastException
        Child noWarnings = (Child) getParent();

        // vs...

        // compile-time failure
        Child compileTimeFailure = new Parent();
    }
    
    private static Parent getParent() {
        return new Parent();
    }

    private static class Parent { }

    private static class Child extends Parent { }

代码片段第 4 行的代码不会产生警告,但会产生ClassCastException. 为什么编译器无法对此进行类型检查?

如果我们添加一个间接层,这会变得更加奇怪:

    public static void main(String[] args) {
        // compile-time OK
        // run-time ClassCastException
        Boxed noWarnings = new Boxed((Child) getParent());

        // vs...
        
        // compile-time OK, but warning is emitted correctly
        // run-time ClassCastException
        Boxed classCastWarning = new Boxed((Child) new Parent());
    }

    private static Parent getParent() {
        return new Parent();
    }

    private static class Boxed {
        public Boxed(Child data) {

        }
    }

    private static class Parent { }

    private static class Child extends Parent { }

编译器不允许第一个示例,但允许第二个示例,这似乎不直观。

任何关于这两种情况的信息都将受到欢迎。

标签: javacasting

解决方案


为什么编译器无法对此进行类型检查?

有些 Parent实例是 的实例Child,因为所有Child的 s 都是Parents。编译器不知道将返回的实例是否为 a Child;但它可能是。所以它允许它。

事实上,正如Stephen C 指出的那样,JLS 不允许编译器将此称为编译错误。

具体来说,语言规范说

如果无法通过强制转换(第 5.5 节)将操作数的编译时类型转换为强制转换运算符指定的目标类型,则会发生编译时错误。

(它还描述了在转换表达式中发生编译时错误的另一种情况;但是这些不适用于这里,因为没有一个或多个AdditionalBound术语)。

Parent可以Child通过缩小引用转换来转换,因此不会发生编译时错误。

Boxed classCastWarning = new Boxed((Child) new Parent());

显然会失败。new Parent()不是 的实例Child,而是 的实例Parent

但是编译器实际上并没有考虑强制转换new Parent()的上下文中的任何事情,除了它是一个 type 的表达式Parent,它必须允许你强制转换为一个Child.

但这并不是说不允许诸如 IDE 之类的工具给你一个警告(甚至是一个错误,如果你有这样的配置):通过静态分析很清楚,这new Parent()将是一个Parent精确的实例(因为这就是全部new Parent()),因此将其转换为子类将失败。您的工具可以告诉您规范中未具体涵盖的内容。


推荐阅读