2012-04-30 91 views
2

下面的代码片段将导致运行时:的Java铸造导致运行时错误而不是编译错误

class Vehicle { 
    public void printSound() { 
     System.out.print("vehicle"); 
    } 
} 

class Car extends Vehicle { 
    public void printSound() { 
     System.out.print("car"); 
    } 
} 

class Bike extends Vehicle { 
    public void printSound() { 
     System.out.print("bike"); 
    } 
} 

public class Test { 
    public static void main(String[] args) { 
     Vehicle v = new Car(); 
     Bike b = (Bike) v; 

     v.printSound(); 
     b.printSound(); 
    } 
} 

我的问题是:为什么是导致运行时错误,但不编译错误?编译器是否应该知道'v'已经是'Car'并且不能被转换成'Bike'?

回答

9

从理论上讲,这将是可能的编译器自言自语地说:“v是局部变量,分配是一个0​​在任何时候之前试图投地Bike它改变它的价值, Car无法成功转换为Bike,因此这是一个错误。“

但是,我知道没有Java编译器会为你做分析。在最简单的情况下它确实是值得的。相反,其行为是编译器会看到剧集,以及可以将Vehicle投射到Bike的原因,因此它允许。

一般来说,这就是演员表达的意思:它告诉编译器,即使这个任务可能失败,你也很确定它不会。作为允许编译代码的交换,您承担运行时异常的风险。

0

No. v是一个Vehicle它可能会将它投射到Bike。编译器的工作不是找出每个对象的实际运行时类型(特别是因为有时这是不可能的)。

1

对象的类型转换发生在运行时间,使编译器doen't承认它

4

从超类铸件可以工作,所以它被允许(编译时)。从一个完全不同的类铸件是不允许的,例如:

Integer a = 1; 
String b = (String)a; // compile error 
String b = (String)(Object)a; // runtime error 
0

的Java的语义说,这必须导致运行时错误。在这种情况下,可以查看代码并确定它在运行时肯定会抛出错误,但编译器如何知道ClassCastException不是您想要的?

像IntelliJ和Eclipse这样的编辑器可以(而且确实)注意到这些类型的错误并警告你,但是Java的规则说这是必须编译的合法代码。

2

对于

R r = /* some code to initialize "r" */ 
T t = (T) r; 

Java语言规范说:

如果R是一个普通的类(不是数组类):

  • 如果T是一个类类型,那么R必须与T的子类或T的子类相同,否则会引发运行时异常。
  • 如果T是接口类型,那么R必须实现接口T,否则会引发运行时异常。
  • 如果T是一个数组类型,则会引发运行时异常。
+0

不错,简单的解释。以供将来参考JLS SE8 https://docs.oracle.com/javase/specs/jls/se8/html/jls-5.html – georger

0

这是因为你已经定义的变量v作为Car运行时错误。您无法将Car转换为Bike

编译器不会检查这种类型的值赋值,因为编译器通常不检查语义。