从超类到子类的显式转换

问题

public class Animal {
    public void eat() {}
}

public class Dog extends Animal {
    public void eat() {}

    public void main(String[] args) {
        Animal animal = new Animal();
        Dog dog = (Dog) animal;
    }
}

赋值Dog dog = (Dog) animal;不会生成编译错误,但在运行时它会生成aClassCastException。为什么编译器无法检测到此错误?


#1 热门回答(246 赞)

通过使用演员你基本上告诉编译器"相信我。我是一个专业人士,我知道我在做什么,我知道虽然你不能保证,但我告诉你,这个animal变量肯定会去成为一只狗。"

由于动物实际上不是狗(它是动物,你可以做Animal animal = new Dog();并且它是狗),VM在运行时抛出异常因为你违反了那个信任(你告诉编译器一切都会好的而且它不是!)

编译器比仅仅盲目地接受所有内容更聪明一点,如果你尝试在不同的继承层次结构中强制转换对象(例如将一个Dog转换为一个String),那么编译器会将它抛回给你,因为它知道它永远不会起作用。

因为你实际上只是停止编译器的抱怨,所以每次你强制转换它都很重要,以确保你不会在if语句中使用instanceof(或者那样的东西)。


#2 热门回答(40 赞)

因为理论上Animal animal可以是一只狗:

Animal animal = new Dog();

一般来说,向下转型不是一个好主意。你应该避免它。如果你使用它,最好包括一个支票:

if (animal instanceof Dog) {
    Dog dog = (Dog) animal;
}

#3 热门回答(36 赞)

为了避免这种ClassCastException,如果你有:

class A
class B extends A

你可以在B中定义一个带有A对象的构造函数。这样我们可以做"强制转换",例如:

public B(A a) {
    super(a.arg1, a.arg2); //arg1 and arg2 must be, at least, protected in class A
    // If B class has more attributes, then you would initilize them here
}