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类扩展了Tree,main()方法在Redwood中,而Tree是一个只有默认构造函数的空类;这个考试问题的答案是“当代码试图将树向下转换为红木时,将抛出ClassCastException。”
我想知道为什么抛出异常以及在哪里。根据我的理解,您可以声明Tree t1 = new Redwood(),那么为什么我不能直接将树投射到Redwood?
答案 0 :(得分:7)
通过使用演员,你基本上告诉编译器"相信我。我是专业人士,我知道我在做什么,我知道虽然你无法保证,但我告诉你这个树变量肯定会是红木。&。 #34;
由于树实际上不是红木(它是一棵树,你可以做Tree tree = new Redwood();而且它是红木)VM会抛出异常运行时因为你违反了那个信任(你告诉编译器一切都会好的而且它不是!)
编译器比仅仅盲目地接受所有内容更聪明一点,如果你尝试在不同的继承层次结构中强制转换对象(例如将红木转换为String),那么编译器会将它抛回给你,因为它知道它永远不会可能会工作。
因为你基本上只是停止编译器的抱怨,所以每次你强制要求你通过在if语句中使用instanceof来检查你是否会导致ClassCastException(或者是那种效果。)
参考http://www.xyzws.com/Javafaq/why-down-casting-throws-classcastexception/125
答案 1 :(得分:2)
每个Redwood
都是Tree
。这就是Tree t2 = (Tree) r1;
有效的原因。
并非每个Tree
都是Redwood
。这就是Redwood r2 = (Redwood) t1;
如果t1
是Tree
而不是Redwood
则无效的原因。
更正式的,请参阅JLS §5.1.6:
六种转换称为缩小参考转换:
从任何引用类型S到任何引用类型T,前提是S是T(§4.10)的正确超类型。
一个重要的特殊情况是,从类类型Object到任何其他引用类型(第4.12.4节)存在缩小的引用转换。
从任何类型C到任何非参数化接口类型K,前提是C不是最终的且不实现K.
从任何接口类型J到任何非最终的非参数化类型C。
从任何接口类型J到任何非参数化接口类型K,前提是J不是K的子接口。
从Cloneable和java.io.Serializable接口类型到任何数组类型T []。
从任何数组类型SC []到任何数组类型TC [],前提是SC和TC是引用类型,并且从SC到TC有一个缩小的引用转换。
此类转换需要在运行时进行测试,以确定实际参考值是否是新类型的合法值。如果没有,则抛出ClassCastException。
答案 2 :(得分:1)
仅仅因为每棵红木都是树,但每棵树都不是红木。编译器足够聪明,可以保护您的程序做错事。
答案 3 :(得分:0)
Tree
类型的变量不包含Tree
类型的对象;相反,它将null
或引用保存到一个保证可用作Tree
对象的对象。同样使用Redwood
类型的变量。
由于Redwood
继承自Tree
,因此保证Redwood
对象可用作Tree
对象,而Tree
类型的变量可能会保留对这样一个对象的引用。如果尝试将类型Tree
的变量强制转换为Redwood
类型的变量,那么如果变量保持null
,那么此类强制转换将成功[因为Redwood
类型的变量可以保存null
],或者如果变量保存对可用作Redwood
的对象的引用[因为该类型的变量可以保存任何此类引用],但如果变量成立则会失败对不能用作Redwood
[{1}}类型的变量的对象的引用不能保存对此类对象的引用]。
请注意,如果您尝试转换Redwood
表达式的结果,系统将表现得好像结果存储在临时变量中,并且应用了强制转换。无论变量是否有任何方式实际上可以保存对可用作new
的对象的引用,编译器仍然可能表现得好像该变量可能或可能不包含这样的引用,并且只发现该变量代码执行时没有。