[英]What are some hardfast “rules” for casting with subtypes?
我正在为我的Java决赛进行一些练习考试,我遇到了这个问题。
请考虑以下类定义,并指出'Test.main()'是否可以成功编译。 如果它确实编译,指示它是否会成功运行,或者如果没有,则指出将抛出什么异常。
public class A {
public int method(int[] a) {...}
}
public class B extends A {
@Override
public int method(int[] a) {...}
}
public class C extends B {
@Override
public int method(int[] a) {...}
public void otherMethod() {...}
}
public class Test {
public static void main(String[] args) {
A a = new C();
B b = new B();
b = (B) a;
}
}
我认为Test.main()会编译但是抛出运行时异常,因为a是实际类型C并且我们正在尝试将它强制转换为类型B.事实并非如此,因为答案说这很好。
我对铸造规则感到非常困惑,其中有一个层次深度超过2个层次。 演讲幻灯片并没有这种信息!
那么,如果在考试中弹出这类问题,请记住哪些难以解决的“规则”?
当存在复杂的层次结构时,尝试将其绘制出来以便更清楚:
A <- B <- C
我认为Test.main()会编译但抛出一个运行时异常,因为a是实际类型C的事实,我们试图将它强制转换为类型B.
a
基础类型是 C
但是, C
可以转换为B
和 A
因为C
继承自B
, B
继承自A
基本上,引用类型转换是否成功的一般规则如下:
对于以下格式的任何演员:
(X)Y
其中
X
是引用类型而Y
是引用类型的变量,如果只能沿箭头的方向沿着Y
的底层类型转到继承层次结构中的X
,则转换将在运行时成功。
说我们有这个代码:
A a = new A();
B b = (B)a;
这将失败,因为我们需要逆着箭头的方向从A
到B
你怎么知道演员阵容是否会在编译时失败呢?
这很容易。 只需检查Y
的变量类型 (不是基础类型!)是否与X
无关。
例如:
// these two types are unrelated
class Foo {}
class Bar {}
// ...
Foo f = new Foo();
Bar b = (Bar)f; // fails to compile
但是,如果Y
的变量类型与X
相关,则编译很好:
Object f = new Foo();
Bar b = (Bar)f; // Bar inherits from Object, so compiles fine.
// But since Foo (f's underlying type) is unrelated to Bar
// this crashes at runtime
完全理解这个看似简单的问题所涉及的问题将需要很长时间,并且需要人们理解Java语言规范 ,但是一个体面的理解可能是一个可以实现的。 @ JBNizet的具体化理念也很有用。
一些术语和简化是有序的:
A
, B
和C
)。 我们将源类型表示为Source
,将目标类型表示为Target
。 =
符号的右侧,而目标类型的变量出现在其左侧。 因此,您可以: Target t = (Target) s
,其中s
是Source
类型的变量。 现在, 根据JLS的适用规则(经过一些简化)是:
对于要编译的这种赋值(
Target t = (Target) s
),Target
必须是Source
的子类(或子类型),或者Source
必须是Target
的子类。
(实际严格的规则是: If T is a class type, then either |S| <: |T|, or |T| <: |S|
。否则,发生编译时错误。 |S|
表示擦除 S
和<:
意味着一个is-subclass-of relation。)
现在,您的类A
, B
和C
创建以下层次结构: C <: B <: A
:
main
方法可以有效地表达为:
A a = new C(); //as-is (1)
// the intervening B b = new B() does not make any difference
B b = (B) a; //as-is (2)
现在,通过适用规则去如上所述,由于所述类型的b
(即Target
),它是B
是的一个子类A
(即Source
),(2)中的分配应编译细,因为要从源类铸件( A
) 到 is-subclass- relationship关系中的目标类( B
)。
根据(1),变量a
的运行时类型实际上是C
并且由于C
是B
的子类,因此任何类型C
变量(源)都可以始终分配给类型B
的变量(目标),以使强制转换在运行时成功(即,它不会抛出ClassCastException
)。 这被称为Liskov替代原则 。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.