详细解答了Michael Berry给出的答案。
Dog d = (Dog)Animal; //编译通过但运行时失败
在这里,您告诉编译器“相信我。我知道 d 实际上是指一个Dog
对象”,尽管它不是。
请记住,当我们进行下转换时,编译器被强制对我们进行信任。
编译器只知道声明的引用类型。JVM在运行时知道对象的真实情况。
因此,当JVM在运行时发现Dog d
实际上是引用Animal
而不是Dog
对象时,它会说。
嘿...你欺骗了编译器,并抛出一个大大的ClassCastException
。
因此,如果您正在进行下转换,则应使用instanceof
测试以避免出错。
if (animal instanceof Dog) {
Dog dog = (Dog) animal;
}
现在有一个问题出现在我们的脑海中。为什么编译器允许下转换,而最终却会抛出java.lang.ClassCastException
?
答案是编译器所能做的就是验证两个类型是否在相同的继承树中,因此取决于下转换之前可能存在的任何代码,animal
可能是dog
类型。
编译器必须允许在运行时可能有效的事情。
考虑以下代码片段:
public static void main(String[] args)
{
Dog d = getMeAnAnimal();
Dog d = (Dog)getMeAnAnimal();
d.eat();
}
private static Animal getMeAnAnimal()
{
Animal animal = new Dog();
return animal;
}
然而,如果编译器确定强制转换不可能成功,编译将失败。也就是说,如果您尝试在不同的继承层次结构中进行对象转换,则会出现错误。
String s =(String)d; // ERROR:无法将Dog转换为String
与向下转型不同,向上转型是隐式完成的,因为当您进行向上转型时,您隐含地限制了可以调用的方法数量,
与向下转型相反,向下转型意味着以后您可能想调用更特定的方法。
Dog d = new Dog();
Animal animal1 = d; // 没有显式强制转换也能正常工作
Animal animal2 = (Animal) d; // 没有显式强制转换也能正常工作
以上两个向上转型都可以正常工作,没有任何异常,因为狗是一种动物,任何动物能做到的事情,狗都能做到。但反过来则不成立。