4

以下代码片段将导致运行时:

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

4

7 回答 7

11

理论上,编译器有可能对自己说: "v是局部变量,它被分配为成功转换为。因此,这是一个错误。”CarBikeCarBike

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

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

于 2012-04-30T17:53:30.200 回答
5

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

Integer a = 1;
String b = (String)a; // compile error
String b = (String)(Object)a; // runtime error
于 2012-04-30T17:50:34.617 回答
4

为了

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

Java 语言规范说:

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

  • 如果 T 是类类型,则 R 必须是与 T 相同的类或 T 的子类,否则将引发运行时异常。
  • 如果 T 是接口类型,则 R 必须实现接口 T,否则将引发运行时异常。
  • 如果 T 是数组类型,则抛出运行时异常。
于 2012-04-30T17:54:07.870 回答
1

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

于 2012-04-30T17:50:07.153 回答
0

No.v是 aVehicle并且有可能将其转换为Bike. 找出每个对象的实际运行时类型不是编译器的工作(尤其是因为有时这是不可能的)。

于 2012-04-30T17:48:37.993 回答
0

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

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

于 2012-04-30T17:52:04.817 回答
0

这是一个运行时错误,因为您已经将变量定义vCar. 您无法转换CarBike.

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

于 2012-04-30T17:54:37.267 回答