请检查下面的程序。
我有疑问时,编译器将发出铸造在编译器级的异常,当它会在runtime
?
像在下面方案,表达
我认为(Redwood) new Tree()
应在编译时已经失败,因为树是不是红木。 但它并不是因为它没有compile time
,按预期的过程中失败runtime
!
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());
}
void go2(Tree t1, Redwood r1) {
Redwood r2 = (Redwood)t1;
Tree t2 = (Tree)r1;
}
}
class Tree { }
编译器将只是看看表达式的编译时类型。 它不会做的运行时类型表达的假设。 new Tree()
具有编译时类型Tree
,所以(Redwood)new Tree()
是没有什么不同的(Redwood)myTreeVariable
。
一棵树不一定是红木,但它可能是,因此缺乏编译时错误的。
在这种情况下,这是很明显它不是,但是编译器会很抱怨的事情,肯定是不正确的,而不是可能或可能不正确。 在这种情况下的逻辑不正确,而不是代码本身,而逻辑是你的问题,不是编译器的。
Tree t = new Redwood();
Redwood r = (Redwood) t;
完善有效的在编译和运行时间。
我在解释加入多一个子类。
Tree
/ \
/ \
/ \
Redwood Blackwood
在这个层次:
向上转型 :当我们在一个方向沿着投类层次结构的引用from the sub classes towards the root
。 我们没有必要在这种情况下使用转换运算符
Upcase实施例:
一个Redwood
或Blackwood
都是tree
:所以
Tree t1;
Tree t2;
Redwood r = new Redwood() ;
Blackwood b = new Blackwood() ;
t1 = r; // Valid
t2 = b; // Valid
垂头丧气的 :当我们在一个方向沿着投类层次结构的引用from the root class towards the children or subclasses
。 我们需要明确的强制类型转换。
沮丧的实施例:
Redwood r = new Tree(); //compiler error, because Tree is not a Redwood
Blackwood r = new Tree(); //compiler error, because Tree is not a Blackwood
你需要明确的类型转换一个Tree object
,如果它的确表明无论是 Redwood
或Blackwook
对象,其他明智它运行时错误。 例如
在这种情况下:
Tree t1;
Tree t2;
Redwood r = new Redwood() ;
Blackwood b = new Blackwood() ;
t1 = r; // Valid
t2 = r; // Valid
Redwood r2 = (Redwood)t1;
Blackwood b2 = (Blackwood)t2
[回答]
Redwood r = (Redwood) new Tree();
为什么没有编译器错误?
其向下转型的例子:
源Redwood r = (Redwood) new Tree();
拳头创建树对象和类型强制转换为Redwood
。
你可以认为这是如下:
Tree t = new Tree();`
Redwood r = (Redwood)t;
所以其确定在编译时,
[回答]
为什么运行时错误?
不过说真的Redwood
子不能指向Tree
晚饭类对象。 所以你的代码无法在运行。
树T =新树();
t
指向Tree()
对象不Redwood()
这是运行时错误的原因。
编译器没有什么是现在的价值t
和语法上的每一件事情就是写。 但在由于运行时间Redwood r = (Redwood)t;
其中t
是的对象Tree class
,如果成为故障。
[建议:]
我想建议你instanceof运算符使用:
铸造/胁迫操作用于类型和instanceof运算符之间的转换是用来检查在运行时类型信息 。*
说明 :
instanceof运算符可以让你决定一个对象的类型。 这需要在操作员和操作员的右侧的类型的左侧的对象,并返回一个布尔值指示对象是否属于类型或没有。 这是最清楚地表明一个例子:
if (t instanceof Redwood)
{
Redwood r = (Redwood)t;
// rest of your code
}
但我也想补充: 从基本型到派生型铸造是一件坏事。 参考 : 谨防instanceof运算符
我有疑问时,编译器将发出铸造在编译器级的异常,当它会在运行时?
严格地说,编译器不“的问题铸造例外”。 你要么得到:
之所以说(Redwood) new Tree()
给出了一个运行时异常,而不是编译错误的是,这正是JLS(第5.5.1节)说应该发生。
具体来说,JLS这样说:
“给定一个编译时引用类型S(源极)和一个编译时引用类型T(目标),流延转换选自S存在如果没有发生编译时错误至T由于以下规则”。
“如果S是一个类型”和“如果T是一个类类型,那么无论| S | <:| T |,或| T | <:| S |否则,发生编译时间错误”。
其中 “| S | <:| T |” 指型S是任一类型的T或T的子类型
在这种情况下,S是Tree
和T是Redwood
,无论是类和Redwood
是一个亚型Tree
......所以没有编译错误。
这是显而易见的,这是“错误的”,这一事实是不相关的。 该JLS说,这是合法的Java,因此,它不应该给编译错误。 (智能编译器会发出警告编译该表达式将始终抛出异常的影响......但是这是一个不同的问题。)
在JLS的规则背后的原因并不在规范中列明,但我想它是这样的:
比较这三个片段:
Redwood r = (Redwood) new Tree();
Tree t = new Tree();
Redwood r = (Redwood) t;
Tree t1 = new Tree(); Tree t2 = new Redwood();
Redwood r = (Redwood) (someCondition ? t1 : t2);
Tree t = gardenStore.purchaseTree();
Redwood r = (Redwood) t;
假设他们定义的第一个片段是一个编译错误:
问题的关键是,一旦你开始要求编译器,以证明有关表达式的动态值的东西,你是大滑坡,导致停机问题。 而如果你的编译错误可选的,那么你得到的可怕状况,其中一个编译器可以说,程序是有效的,而另一个可以说,它有误!
引用的树可能是一个红木事前这就是为什么代码编译好。
Tree t1 = new Redwood();
Redwood r1 = (Redwood)t1;
Tree t2 = (Tree)r1;
您可以看到树()有可能已经是红木。
有必要补充一点,没有必要向下转型:由于红木也是一棵树,你可以随时将其分配到一棵树变量,
Redwood r;
Tree t = r; // no casting needed
您可以随时看在Java类型系统替代原则 。 我相信thare是材料亿吨。