从超类到子类的显式转换

sar*_*nan 140 java casting classcastexception

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

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

Mic*_*rry 292

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

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

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

因为你基本上只是停止编译器的抱怨,所以每次你强制转换它都很重要,以确保你不会在if语句中ClassCastException使用by instanceof(或者那种效果).

  • 我喜欢你听起来有多戏剧性 (47认同)
  • @delive你当然会这样做,但是根据这个问题,`Dog`*确实*来自`Animal`! (3认同)

Boz*_*zho 48

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

Animal animal = new Dog();
Run Code Online (Sandbox Code Playgroud)

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

if (animal instanceof Dog) {
    Dog dog = (Dog) animal;
}
Run Code Online (Sandbox Code Playgroud)

  • 是的,因为Animal是超类.不是每只动物都是狗,对吗?您只能通过类型或超类型来引用类.不是他们的亚型. (3认同)

Cau*_*ons 40

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

class A
class B extends A
Run Code Online (Sandbox Code Playgroud)

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

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
}
Run Code Online (Sandbox Code Playgroud)


Zee*_*han 21

Dog d = (Dog)Animal; //Compiles but fails at runtime

在这里,您要对编译器说"信任我.我知道d实际上是指一个Dog对象",尽管它不是. 请记住编译器在进行向下转换时必须信任我们.

编译器只知道声明的引用类型.运行时的JVM知道对象到底是什么.

因此,当运行时的JVM发现Dog d它实际上是指一个Animal而不是Dog它所说的对象时.嘿......你骗了编译器,扔了一个大胖子ClassCastException.

因此,如果你是翘曲,你应该使用instanceof测试来避免搞砸.

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

现在我们想到了一个问题.为什么地狱编译器在最终投掷时会允许向下转换java.lang.ClassCastException

答案是所有编译器都可以做的是验证这两种类型是否在同一个继承树中,因此根据在向下转换之前可能出现的代码,它可能animal是类型的dog.

编译器必须允许可能在运行时工作的东西.

考虑以下代码snipet:

public static void main(String[] args) 
{   
    Dog d = getMeAnAnimal();// ERROR: Type mismatch: cannot convert Animal to Dog
    Dog d = (Dog)getMeAnAnimal(); // Downcast works fine. No ClassCastException :)
    d.eat();

}

private static Animal getMeAnAnimal()
{
    Animal animal = new Dog();
    return animal;
}
Run Code Online (Sandbox Code Playgroud)

但是,如果编译器确定转换不可行,则编译将失败.IE如果您尝试在不同的继承层次结构中转换对象

String s = (String)d; // ERROR : cannot cast for Dog to String

与向下转换不同,向上转换是隐式的,因为当你向上转换时,你隐式地限制了你可以调用的方法的数量,与向下转换相反,这意味着稍后,你可能想要调用更具体的方法.

Dog d = new Dog(); Animal animal1 = d; // Works fine with no explicit cast Animal animal2 = (Animal) d; // Works fine with n explicit cast

上面这两个上传都会正常工作,没有任何例外,因为狗是动物可以做的狗IS-A动物,狗可以做.但事实并非如此.反之亦然.