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
。
编译器不会检查这种值赋值,因为编译器通常不检查语义。