我对java有点陌生,我有一个关于多态性和可能的错误的问题。假设我们有:
public interface Animal {
}
public abstract class Cat implements Animal{
}
public abstract class Fish implements Animal {
}
public class Salmon extends Fish{
}
public class Tiger extends Cat{
}
和假设我们有这样的东西:
Animal t1 = new Tiger();
Fish f1 = new Salmon();
Tiger t2= new Tiger();
Salmon s1 = new Salmon();
以下几行错误是什么(编译时错误,运行时错误或无错误):
Cat c1 = new Cat();
Cat c2 = (Tiger) t1;
Animal a1 = s1;
Animal a2 = new Animal();
Fish f1 = (Fish) t2;
Animal a3 = (Fish) s1;
Animal a4 = (Cat) new Tiger();
Cat c3 = (Cat) new Salmon();
我已经像咆哮一样回答了它,但我认为它有点奇怪,我没有发现运行时错误。如果他们都是正确的,你可以做一个例子,我们有运行时错误(在这个多态性的概念)
我的回答:
a compile error
b no error
c no error
d compile error
e compile error
f no error
g no error
h compile error
专注于"如何在运行时获得异常"的部分。
你认为Cat c3 = (Cat) new Salmon();
是错误的。显然,编译器已经告诉你了。
为什么?因为编译器可以"看到"你创建了一个Salmon,然后你想把它当作Cat,这是没有意义的。
你唯一需要做的就是"过去"编译器要"隐藏";这个事实,比如:
Salmon s1 = new Salmon();
Animal a3 = (Fish) s1;
Cat c3 = (Cat) a3;
一旦你引入a3
,你就可以"隐藏"。事实上,s1
实际上是一个鲑鱼。
当然即使在我的例子中,一个更聪明的编译器也能理解a3
一定是一只鲑鱼,而不是一只猫。但是java把它玩得"简单而保守"。在这里。编译器只识别最基本的类型转换违规,无论好坏,java语言忽略了在编译时也可以检测到的许多情况。这使得实现编译器更容易,但代价是您的代码在运行时更容易暴露于此类异常。
由于Java只支持类的单继承,因此它可以验证类之间的强制转换是否可能有效,因为在强制转换时,其中一个类必须是另一个类的祖先。您可以向上转换,例如Tiger
到Cat
,也可以向下转换,例如Cat
到Tiger
。
我忽略了愚蠢的身份转换,例如Tiger
到Tiger
,但它当然也是允许的。
不能将一个类强制转换为另一个不"相关"的类。
。对于接口,编译器不能验证,因为允许多重继承,这意味着在编译时,总是可以将类或接口强制转换为接口,也总是可以将接口强制转换为类或另一个接口。只能在运行时验证强制转换。
由于没有两个类从同一个基类继承,因此不能为类之间的强制转换设置运行时错误。编译器总是会捕获这个错误。
由于您只有一个接口,并且所有的类都实现了该接口,因此您不能设置将转换为接口的运行时错误。
这意味着要设置一个运行时错误,你需要将从一个接口转换为一个真实对象不兼容的类,例如从Animal t1
(真实对象Tiger
)转换为Salmon
或Fish
。
Salmon s2 = (Salmon) t1; // ClassCastException: class Tiger cannot be cast to class Salmon
你的答案都是正确的。较新的编译器应该能够找到在您的示例中强制转换失败的情况。运行时异常只会在编译器将失去实际类型跟踪的情况下抛出,因为类型是向下转换的:
public Cat catterize(Animal a) {
return (Cat) a; // this line should yield an unsafe typecast warning!
}
...
Salmon salmon = new Salmon();
Cat cat = catterize(salmon); // This is compiletime legal, but will ultimately throw a ClassCastException.