阐述迈克尔·贝瑞(Michael Berry)给出的答案。
Dog d = (Dog)Animal; //Compiles but fails at runtime
在这里,您对编译器说的是“请相信我。我知道d
这实际上是在指Dog
对象”,尽管不是。
请记住,当我们进行向下转换时,编译器被迫信任我们。
编译器仅知道声明的引用类型。运行时JVM知道对象的真正含义。
因此,当运行时JVM弄清楚JVM Dog d
实际上是指an Animal
而不是Dog
对象时,它就会说。嘿...您对编译器撒谎并抛出了一个大胖子ClassCastException
。
因此,如果您垂头丧气,则应该使用instanceof
测试来避免搞砸。
if (animal instanceof Dog) {
Dog dog = (Dog) animal;
}
现在我们想到一个问题。为何地狱编译器最终将抛出时允许向下转换java.lang.ClassCastException
?
答案是,编译器所能做的就是验证这两种类型是否在同一继承树中,因此根据向下转换之前可能附带的任何代码,该animal
类型可能是type dog
。
编译器必须允许在运行时可能起作用的内容。
考虑以下代码片段:
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;
}
但是,如果编译器确定强制转换将无法进行,则编译将失败。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 Animal(动物可以做,狗可以做)因此毫无例外。但这并不是真的。