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
,为什么编译器检测不到这个错误?
8条答案
按热度按时间3lxsmp7m1#
通过使用强制类型转换,你实际上是在告诉编译器,“相信我,我是专业人士,我知道我在做什么,我知道,尽管你不能保证,但我告诉你,这个
animal
变量肯定会是一只狗。”由于动物实际上不是狗(它是动物,您可以执行
Animal animal = new Dog();
,它将是狗),VM在运行时抛出异常,因为您违反了这种信任(您告诉编译器一切都会好的,但它不是!)编译器比盲目地接受一切要聪明一些,如果你试图在不同的继承层次中转换对象(例如,将一个Dog转换为一个String),那么编译器会将它扔还给你,因为它知道这永远不可能工作。
因为您实际上只是在阻止编译器发出抱怨,所以每次进行强制类型转换时,都必须检查在if语句中使用
instanceof
(或类似的语句)是否会导致ClassCastException
。xpszyzbs2#
因为理论上
Animal animal
可以是狗一般来说,向下转换不是一个好主意。您应该避免它。如果您使用它,最好包括一个检查:
5gfr0r5j3#
为了避免此类ClassCastException,如果您有:
你可以在B中定义一个构造函数,它接受A的一个对象。这样我们就可以进行“强制转换”,例如:
iugsix8n4#
第一个月
在这里,您对编译器说“相信我。我知道
d
确实引用了Dog
对象”,尽管它不是。请记住,当我们执行向下转换时,编译器被迫信任我们。编译器只知道声明的引用类型,而JVM在运行时知道对象的真实情况。
因此,当JVM在运行时发现
Dog d
实际上引用的是Animal
,而不是Dog
对象时,它会说:嘿......你对编译器撒了谎,抛出了一个很大的ClassCastException
。因此,如果你是向下预测,你应该使用
instanceof
测试,以避免搞砸。if (animal instanceof Dog) { Dog dog = (Dog) animal; }
现在一个问题出现在我们的脑海中,为什么编译器最终会抛出一个
java.lang.ClassCastException
,却允许向下转换?答案是,编译器所能做的就是验证这两种类型是否在同一个继承树中,因此根据向下转换之前可能出现的代码,
animal
可能是dog
类型。编译器必须允许在运行时可能工作的东西。
考虑以下代码片段:
然而,如果编译器确信强制转换不可能工作,编译就会失败。也就是说,如果你试图在不同的继承层次结构中强制转换对象
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
以上两种向上转换都可以无一例外地工作,因为狗是动物,动物能做的事,狗也能做,但反之则不然。
jexiocij5#
为了推导@Caumons的答案:
假设一个父类有很多子类,并且需要在该类中添加一个公共字段。如果您考虑上述方法,则应该逐个访问每个子类,并为新字段重构它们的构造函数。因此,该解决方案在此场景中不是一个有前途的解决方案
现在来看一下这个解决方案。
父亲可以从每个孩子那里接收一个self对象。下面是一个父亲类:
下面是我们的子类,它应该实现它的父构造函数之一,在本例中是前面提到的构造函数:
现在我们测试应用程序:
结果如下:
父字段为:父字符串
子字段为:子字符串
现在您可以轻松地向父类添加新字段,而不必担心子类代码被破坏;
w8rqjzmb6#
代码生成编译错误,因为您的示例类型是Animal:
Java中不允许向下转换有几个原因。详细信息请参见此处。
kd3sttzy7#
如前所述,这是不可能的。如果你想使用子类的一个方法,评估一下把这个方法添加到超类(可能是空的)中的可能性,然后从子类中调用,得到你想要的行为(子类),这要归功于多态性。所以当你调用d.method()时,调用会成功,而不需要强制类型转换,但是如果对象不是一只狗,就不会有问题了
nbysray58#
如前所述,您不能从超类强制转换到子类,除非您的对象首先从子类示例化。
不过,也有一些变通办法。
您所需要的只是一组构造函数和一个方便的方法,该方法可以将对象强制转换为Dog,也可以返回一个具有相同Animal属性的新Dog对象。
下面是一个例子,它可以做到这一点: