将一个超类转换为子类

3

想象一个类 Cottage 扩展了 Building,并且有如下代码:

Building building = new Building();
Cottage cottage = (Cottage)building;

现在,考虑到Java继承的性质,Building无法转换为Cottage是完全有道理的,但对我而言不合理的是为什么这会编译。为什么它会编译然后抛出运行时的ClassCastException
显然,在实际运行程序之前,building是对Building对象的引用,这不是很明显吗?
作为一个如此通用的问题,我知道我正在寻找可能的重复问题 :) 但我找不到关于“为什么会编译”这个问题的答案 :) 编辑2:我已经接受了一个很好的答案(更不用说下面的讨论了: )),但我仍然认为在Java转换导致运行时错误而不是编译错误中接受的答案最有趣... 编辑:我编辑了IllegalCastException并更正了为ClassCastException

2
很多“显然错误”的东西在Java中编译通过,例如int x = 5 / 0 - gefei
1
强制转换运算符明确告诉编译器不要进行类型安全检查。 - SLaks
标题有点不同,但这完全是我的问题的副本。谢谢... 此外,@dlev 在那里给出的解释很好... - luigi7up
没有所谓的“IllegalCastException” - 应该是“ClassCastException”。 - Jesper
3个回答

10

那是因为编译器不知道你的引用Building指向哪个对象。

因此,在下面这种情况中,当你有一个指向子类对象的基类引用时:-

Building building = new Cottage();
Cottage cottage = (Cottage)building;

它可以完全正常工作。因此,是否为有效的转换是完全由运行时决定的。因此,编译器不会因此抛出错误。

在实际运行程序之前,难道构建不是指向Building对象的引用吗?

不是。 绝对不是。所引用的对象类型直到运行时才能确定。请记住,编译器始终检查引用类型,而实际对象类型在运行时检查。

这个概念被称为多态性,可以将相同的引用类型指向各种子类型的对象。您可以通过谷歌搜索获得大量可供阅读的资源。


1
嗯... 叫我愚蠢吧,但我们都知道,阅读这两行代码时每个引用所持有的内容以及编译器也知道... 当你使用例如持有对象的集合并且期望某种类型时,异常是有意义的,因此您需要进行强制转换,但在这种简单情况下,它应该可以告诉您。在“可能重复”的https://dev59.com/zWPVa4cB1Zd3GeqP4Uyl @devl非常好地解释了它。 - luigi7up
有点被“这个概念被称为多态性”这个评论冒犯了 :( - luigi7up
@luigi7up.. 我不太理解你的第一条评论。而且为什么在你的第二条评论中感到被冒犯了? - Rohit Jain
@luigi7up,看看这个教程 - http://docs.oracle.com/javase/tutorial/java/IandI/polymorphism.html - Rohit Jain
@luigi7up..编译器也是如此。不,编译器并不知道引用所指的实际类型。正如我所说,编译器只检查引用类型是否有效。例如,如果您执行以下转换:Number num = (Number)(date);,那么编译器显然会抱怨,因为它知道无论使用哪种Date实现,都不能将Date引用强制转换为Number引用。因此,它肯定会在运行时失败,并因此给出错误。 - Rohit Jain
正如我所说,编译器只检查引用类型是否有效。在像这样简单的情况下,它可以通过“反射”代码来为我们检查它...非常感谢您如此快速的回复... - luigi7up

3

类型转换的一部分功能是让编译器保持沉默,即你在做类型转换时认为你比编译器更清楚你要转换的对象是什么。如果类型转换不准确,你会得到一个异常。

也许你以为编译器正在跟踪对象指向的内容,并计算引用是否合理。编译器并没有那么聪明,很容易被愚弄,看这个片段:

    int i = 1;
    System.out.println((Number)i);        
    Object o = i;
    System.out.println((String)i); // won't compile (primitive-to-ref-type cast)
    System.out.println((String)o); // compiles (thanks to autoboxing)

编译器只关心如何防止原始类型和引用类型之间的转换。

这有意义并且有点帮助 :) - luigi7up

2
考虑创建一个具有建筑(超类)引用的小屋(子类)对象。
Building b= new Cottage();
Cottage c = (Cottage) b;

以上代码是合法的,因此编译器在运行时不知道引用变量 c 所引用的对象是什么,因此编译器不会抛出错误。
为什么它可以编译通过,但却会抛出运行时IllegalCastException?
顺便提一下,当你尝试将超类对象强制转换为子类对象时,它会抛出ClassCastException,而不是IllegalCastException。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接