当代码尝试将 Tree 向下转型为 Redwood 时,将抛出 ClassCastException

ClassCastException will be thrown when the code attempts to downcast a Tree to a Redwood

在下面的代码中,在第 7 行将 Tree 向下转换为 Redwood 时没有错误,但是为什么在将 Tree 向下转换为 Redwood 时在第 10 行出现运行时错误

public class Redwood extends Tree {
    public static void main(String[] args) {
        new Redwood().go();
    }
    void go() {
        go2(new Tree(), new Redwood());
        go2((Redwood) new Tree(), new Redwood());// no error here
    }
    void go2(Tree t1, Redwood r1) {
        Redwood r2 = (Redwood)t1;// runtime error here
        Tree t2 = (Tree)r1;
    }
}
class Tree { }

嗯,虽然 Redwood 实例始终是 Tree 实例,但并非所有 Tree 实例都是 Redwood。当你创建一个 Tree 实例(带有 new Tree())时,它肯定不是一个 Redwood 实例,并且不能转换为 Redwood.

t1 不是 Redwood 时,

Redwood r2 = (Redwood)t1; 抛出 ClassCastException。在第一次调用 go2 时,第一个参数是 new Tree(),它不是 Redwood.

哦,你在这里看到错误的原因 - Redwood r2 = (Redwood)t1;(第 10 行)而不是第 7 行(go2((Redwood) new Tree(), new Redwood());)是第 10 行首先执行,在第一次调用go2()(第 6 行)。如果注释第 6 行,则会在第 7 行出现异常。

您不能将 Tree 类型的对象转换为 Redwood,因为它不是那种类型。如果父类型的变量实际上是作为该子类型创建的,则只能将其转换为其中一个子类型。

这是合法的,例如:

Tree t = new Redwood();
((Redwood)t).someMethod();

但是你不能转换父类型的对象,所以你这样做是不可能的:

Tree t = new Tree();
((Redwood)t).someMethod();

对象不是基于Redwood的预留内存,也不是构造为一个,所以不可能突然是一个。

由于转换在某些情况下是合法的,编译器将允许它。在 运行 时,当从第 6 行调用它时,您将 运行 首先进入第 10 行的问题,因为逐行调试会显示。

编译器可能足够聪明,可以在编译时告知第 7 行永远不会工作,但它没有实现。

在第 7 行你只是传递了参数,实际的赋值是在第 9 行完成的,相当于下面的代码: Tree T1= (Redwood)new Tree(); // 这很好,因为 base class 可以保存 derive class 的对象以及 base class。但是在第 10 行中,你将 T1 分配给 R2,其中 T1 实际上持有基 class 的对象,你试图将其强行分配给 Redwood 引用(通过强制转换),java 不允许这样做,就好像您将从这个 R2 引用调用 Redwood class(不在 Tree 中)的方法,而不是它无法调用,因为它实际上包含基础 class 对象。因此 class强制转换异常。

好的,这里是一个简化版本的解释,帮助我理解了整个事情: 规则是:这将起作用:

  • 父 p = new 子 ();
  • 子c = new Child();

但这行不通:

  • 子 c = new Parent();