2011-02-01 57 views
96
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;不会产生编译错误,但在运行时会生成一个ClassCastException。为什么编译器不能检测到这个错误?显式转换的子类

+41

您正在告诉编译器不检测错误。 – Mauricio 2011-02-01 13:14:02

回答

222

通过使用一个强制转换,你实际上告诉编译器“相信我,我是一名专业人员,我知道我在做什么,我知道虽然你不能保证,但我告诉你这个animal变量肯定会成为一只狗。“由于动物实际上并不是一只狗(它是一种动物,你可以做Animal animal = new Dog();并且它会成为一只狗),因此VM在运行时会抛出一个异常,因为你违反了该信任(你告诉编译器一切)

编译器比只是盲目地接受所有东西要聪明一点,如果你尝试在不同的继承层次结构中投掷对象(例如将一个Dog转换为String),那么编译器会抛出它回到你身边,因为它知道这绝不可能奏效。

因为你基本上只是从停止抱怨编译器,每次你投来检查,你不会引起ClassCastException是很重要的在if语句(或诸如此类的话。)

38

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

Animal animal = new Dog(); 

一般而言,向下转换是不是一个好主意。你应该避免它。如果你使用它,你最好包括检查:

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

但下面的代码会生成编译错误Dog dog = new Animal(); (不兼容的类型)。但在这种情况下编译器识别动物是超级类,而狗是子类。所以这个任务是错误的。但是当我们投狗狗=(狗)动物;它接受。请向我解释这个 – saravanan 2011-02-01 13:19:07

+3

是的,因为动物是超类。不是每个动物都是狗,对吧?你只能通过它们的类型或它们的超类型来引用类。不是他们的亚型。 – Bozho 2011-02-01 13:31:23

+1

所以编译器相信我们只在铸造..是正确的 – saravanan 2011-02-01 13:39:57

1

的代码生成编译错误,因为你实例类型是一种动物:

Animal animal=new Animal(); 

向下转换为Java不允许有以下几个原因。 有关详细信息,请参阅here

30
使用 instanceof

为了避免这种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 
} 
17

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

在这里,你对编译器说:“相信我。我知道d真的指的是Dog对象”,但事实并非如此。 记得编译器是被迫当我们做一个沮丧的信任我们。

编译器只知道声明的引用类型,JVM在运行时知道对象究竟是什么。

因此,当在运行时的JVM出这个Dog d实际上是指一个Animal而不是Dog对象,它说。 嘿......你对编译器撒谎了,并且抛出了一个大胖子ClassCastException

所以,如果你是向下倾斜,你应该使用instanceof测试,以避免搞砸。

现在,一个问题涉及到我们的心灵。为什么地狱编译器在最终抛出一个java.lang.ClassCastException时允许downcast?

答案是,所有的编译器可以做的是验证两种类型在同一个继承树,所以这取决于什么代码可能已经来过了就垂头丧气 ,它可能是animaldog类型。

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

考虑下面的一小段代码片段:

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; 
} 

但是,如果编译器是确保投将无法工作,编译将失败。 I.E.如果试图投在不同的继承对象层次结构

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

上述两个向上转型将正常工作,没有任何异常,因为狗是-A的动物,anithing的动物可以做到,一只狗能做到。但这不是真的,反之亦然。

1

如上所述,这是不可能的。 如果您想要使用子类的方法,请评估将方法添加到超类(可能为空)的可能性,并且由于多态性而从子类获取您想要的行为(子类)。 所以当你调用d.method()时,调用会成功执行,但是如果对象不是狗,那么不会有问题