在编译时和运行时的向下转换/上溯造型错误?(Downcasting/Upcasting error

2019-07-17 21:31发布

请检查下面的程序。

我有疑问时,编译器将发出铸造在编译器级的异常,当它会在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 { }

Answer 1:

编译器将只是看看表达式的编译时类型。 它不会做的运行时类型表达的假设。 new Tree()具有编译时类型Tree ,所以(Redwood)new Tree()是没有什么不同的(Redwood)myTreeVariable



Answer 2:

一棵树不一定是红木,但它可能是,因此缺乏编译时错误的。

在这种情况下,这是很明显它不是,但是编译器会很抱怨的事情,肯定是不正确的,而不是可能或可能不正确。 在这种情况下的逻辑不正确,而不是代码本身,而逻辑是你的问题,不是编译器的。

Tree t = new Redwood();
Redwood r = (Redwood) t;

完善有效的在编译和运行时间。



Answer 3:

我在解释加入多一个子类。

       Tree
      /    \
     /      \
    /        \ 
Redwood       Blackwood  

在这个层次:

向上转型 :当我们在一个方向沿着投类层次结构的引用from the sub classes towards the root我们没有必要在这种情况下使用转换运算符

Upcase实施例:

一个RedwoodBlackwood都是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 ,如果它的确表明无论是 RedwoodBlackwook对象,其他明智它运行时错误。 例如

在这种情况下:

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运算符



Answer 4:

我有疑问时,编译器将发出铸造在编译器级的异常,当它会在运行时?

严格地说,编译器不“的问题铸造例外”。 你要么得到:

  • 编译错误,或
  • 运行时异常。

之所以说(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;

假设他们定义的第一个片段是一个编译错误:

  • 那么关于第二个? 嗯,这很容易证明了。

  • 怎么样,第三个? 这可能是容易......也可能是极度困难。

  • 怎么样的第四个? 现在片段的合法性取决于我们甚至可能没有源代码的方法的语义!

问题的关键是,一旦你开始要求编译器,以证明有关表达式的动态值的东西,你是大滑坡,导致停机问题。 而如果你的编译错误可选的,那么你得到的可怕状况,其中一个编译器可以说,程序是有效的,而另一个可以说,它有误!



Answer 5:

引用的树可能是一个红木事前这就是为什么代码编译好。

     Tree t1 =  new Redwood(); 
     Redwood r1 = (Redwood)t1;
     Tree t2 = (Tree)r1;

您可以看到树()有可能已经是红木。



Answer 6:

有必要补充一点,没有必要向下转型:由于红木也是一棵树,你可以随时将其分配到一棵树变量,

Redwood r;
Tree t = r; // no casting needed

您可以随时看在Java类型系统替代原则 。 我相信thare是材料亿吨。



文章来源: Downcasting/Upcasting error at compile time & runtime?