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’吗?

从理论上讲,编译器可能会对自己说:“ v是局部变量,被指定为Car 。在尝试强制转换为Bike之前,它没有改变它的值,也没有办法让Car成功投入Bike 。因此,这是一个错误。“

但是,我知道没有Java编译器可以为您进行分析。 在最简单的情况下,这真的是值得的。 相反,行为是编译器看到转换,以及可以将Vehicle转换为Bike ,因此它允许它。

一般来说,这就是一个强制转换的意思:它告诉编译器即使这个赋值可能会失败,你也很确定它不会。 作为允许代码编译的交换,您承担运行时exception的风险。

从超类转换可能有效,因此允许(在编译期间)。 不允许从完全不同的类进行投射,例如:

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

对于

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

Java语言规范说:

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

  • 如果T是类类型,则R必须是与T相同的类或T的子类,否则抛出运行时exception。
  • 如果T是接口类型,则R必须实现接口T,否则抛出运行时exception。
  • 如果T是数组类型,则抛出运行时exception。

对象的类型转换在运行时发生,因此编译器无法识别它

不, v是一辆Vehicle ,也许可以把它投到Bike 。 找出每个对象的实际运行时类型并不是编译器的工作(特别是因为有时这是不可能的)。

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

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

这是运行时错误,因为您已将变量v定义为Car 。 你无法将Car改装成Bike

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