显式从超类转换为子类
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
。 为什么编译器不能检测到这个错误?
通过使用一个演员,你基本上告诉编译器“相信我,我是一个专业人士,我知道我在做什么,我知道虽然你不能保证,但我告诉你,这个animal
variables绝对会成为一只狗“。
由于动物实际上不是一只狗(它是一种动物,所以你可以做Animal animal = new Dog();
它会是一只狗),VM在运行时会抛出一个exception,因为你违反了这个信任编译器一切都会好起来的,而不是!)
编译器比只是盲目地接受所有东西要聪明一点,如果你尝试在不同的inheritance层次结构中投掷对象(例如将Dog转换为String),那么编译器会把它扔回给你,因为它知道永远不可能工作。
因为你基本上只是停止编译器的抱怨,所以每次ClassCastException
,检查你不会导致一个ClassCastException
在if语句中使用instanceof
(或类似的东西)。
因为从理论上说, Animal animal
可以是一只狗:
Animal animal = new Dog();
一般来说,向下转换不是一个好主意。 你应该避免它。 如果你使用它,你最好包括一个支票:
if (animal instanceof Dog) { Dog dog = (Dog) animal; }
为了避免这种ClassCastException,如果你有:
class A class B extends A
你可以在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 }
Dog d = (Dog)Animal; //Compiles but fails at runtime
在这里,你正在对编译器说:“相信我,我知道d
实际上指的是一个Dog
对象”,但事实并非如此。 记住编译器被迫信任我们,当我们做一个downcast 。
编译器只知道声明的引用types。 运行时的JVM知道对象究竟是什么。
所以当运行时的JVM发现Dog d
实际上是指Animal
而不是Dog
对象的时候。 嘿…你骗了编译器,并抛出一个大胖的ClassCastException
。
所以,如果你是downcasting你应该使用instanceof
testing,以避免搞砸。
if (animal instanceof Dog) { Dog dog = (Dog) animal; }
现在我们想到了一个问题。 为什么地狱编译器最终会抛出一个java.lang.ClassCastException
?
答案是所有编译器都可以做的是validation这两种types是在同一个inheritance树中,所以根据在downcast之前可能出现的任何代码,可能是animal
是dog
types的。
编译器必须允许可能在运行时工作的东西。
考虑下面的代码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; }
但是,如果编译器确定该转换不可行,则编译将失败。 IE如果您尝试在不同的inheritance层次结构中投射对象
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
上述两个upcast都能正常工作,没有任何exception情况,因为一只狗可以做一只动物。 但这不是真的,反之亦然。
该代码生成一个编译错误,因为您的实例types是一个动物:
Animal animal=new Animal();
Java中不允许向下转换,原因有几个。 详情请看这里 。
如上所述,这是不可能的。 如果要使用子类的方法,请评估向超类添加方法的可能性(可能为空),并由于多态性而从子类调用,以获取所需的行为(子类)。 所以当你调用d.method()时,调用会成功执行,但是如果对象不是狗,那么就不会有问题