【问题标题】:Java Class Casting RulesJava 类转换规则
【发布时间】:2018-10-05 05:05:13
【问题描述】:
    public static void main(String[] args) {
       new Redwood().go();
    }
    void go() {
       go2(new Tree(), new Redwood());
       go2((Redwood) new Tree(), new Redwood());
    }
    void go2(Tree t1, Redwood r1) {
       Redwood r2 = (Redwood)t1;
       Tree t2 = (Tree)r1;
    }
}

给定类 Redwood extends Tree,main() 方法在 Redwood 中,Tree 是一个只有默认构造函数的空类;这个考试问题的答案是“当代码尝试将 Tree 向下转换为 Redwood 时,将引发 ClassCastException。”

我想知道抛出异常的原因和位置。根据我的理解,您可以声明 Tree t1 = new Redwood() 那么为什么我不能直接将 Tree 转换为 Redwood?

【问题讨论】:

  • 因为每只腊肠犬都是狗,但不是每只狗都是腊肠犬。
  • @Loc 为什么不是“Java 转换规则”?它是在 JLS 中定义的,所以它是 Java,它是规则,它是关于强制转换的。

标签: java class inheritance casting polymorphism


【解决方案1】:

通过使用强制转换,您实际上是在告诉编译器“相信我。我是专业人士,我知道自己在做什么,而且我知道尽管您不能保证,但我告诉您,这树变量肯定会是红木。”

由于树实际上不是红木(它是一棵树,您可以执行 Tree tree = new Redwood(); 它会是一棵红木)VM 在运行时抛出异常,因为您违反了该信任(你告诉编译器一切都会好起来的,但事实并非如此!)

编译器比盲目地接受所有东西要聪明一点,如果你尝试将对象转换为不同的继承层次结构(例如将红木转换为字符串),那么编译器会将它扔给你,因为它知道永远不可能可能工作。

因为您实际上只是在阻止编译器抱怨,所以每次您进行强制转换时,重要的是检查您不会在 if 语句中使用 instanceof(或类似的东西)导致 ClassCastException。

参考http://www.xyzws.com/Javafaq/why-down-casting-throws-classcastexception/125

【讨论】:

    【解决方案2】:

    每个Redwood 都是Tree。这就是Tree t2 = (Tree) r1; 起作用的原因。

    并非每个Tree 都是Redwood。这就是为什么如果t1Tree 而不是RedwoodRedwood r2 = (Redwood) t1; 将不起作用。

    更正式的,见JLS §5.1.6

    六种转换称为缩窄参考转换:

    • 从任何引用类型 S 到任何引用类型 T,前提是 S 是 T 的适当超类型(第 4.10 节)。

    • 一个重要的特殊情况是从类类型 Object 到任何其他引用类型的缩小引用转换(第 4.12.4 节)。

    • 从任何类类型 C 到任何非参数化接口类型 K,前提是 C 不是 final 且不实现 K。

    • 从任何接口类型 J 到任何非 final 的非参数化类类型 C。

    • 从任何接口类型 J 到任何非参数化接口类型 K,前提是 J 不是 K 的子接口。

    • 从接口类型 Cloneable 和 java.io.Serializable 到任何数组类型 T[]。

    • 从任何数组类型 SC[] 到任何数组类型 TC[],前提是 SC 和 TC 是引用类型,并且存在从 SC 到 TC 的缩小引用转换。

    此类转换需要在运行时进行测试,以确定实际参考值是否是新类型的合法值。如果没有,则抛出 ClassCastException。

    【讨论】:

    • ?我认为你的演员表和变量倒退了。
    • @AJNeufeld 啊,是的,你是对的。谢谢,已更正。
    • 但是 t1 是一棵树,并且在 Redwood r2 = (Redwood) t1 中被转换为 Redwood?
    • @CianBarrett 不确定你在问什么,但如果t1 不是Redwood 的实例,Redwood r2 = (Redwood) t1; 会抛出 CCE。
    • @lexicore 抱歉,在您进行编辑之前,我已经写过了!感谢您的澄清!
    【解决方案3】:

    仅仅是因为每棵红木都是一棵树,但每棵树都不是红木。编译器足够聪明,可以保护您的程序做错事。

    【讨论】:

      【解决方案4】:

      Tree 类型的变量不包含Tree 类型的对象;相反,它持有null 或一个对象的reference,该对象保证可用作Tree 对象。 Redwood 类型的变量也是如此。

      因为Redwood 继承自Tree,所以保证Redwood 对象可用作Tree 对象,并且Tree 类型的变量可能持有对此类对象的引用。如果尝试将Tree 类型的变量转换为Redwood 类型的变量,则如果该变量持有null [因为Redwood 类型的变量可以持有null],则这种转换将成功,或者如果变量包含对可用作 Redwood 的对象的引用 [因为该类型的变量可以包含任何此类引用],但如果变量包含对不可用作 @ 的对象的引用,则会失败987654337@ [Redwood 类型的变量不能保存对此类对象的引用]。

      请注意,如果您尝试强制转换 new 表达式的结果,系统将表现得好像该结果存储在临时变量中,并且已应用强制转换。无论变量是否有任何方式可以实际保存对可用作Redwood 的对象的引用,编译器仍可能表现得好像该变量可能持有或可能不持有这样的引用,只是发现它没有't 当代码执行时。

      【讨论】:

        猜你喜欢
        • 2015-09-29
        • 1970-01-01
        • 2013-04-10
        • 2016-08-10
        • 2015-07-27
        • 1970-01-01
        • 1970-01-01
        • 2015-08-16
        • 1970-01-01
        相关资源
        最近更新 更多