繁体   English   中英

使用子类型进行铸造有哪些硬性“规则”?

[英]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继承自BB继承自A

基本上,引用类型转换是否成功的一般规则如下:

对于以下格式的任何演员:

(X)Y

其中X是引用类型而Y是引用类型的变量,如果只能沿箭头的方向沿着 Y的底层类型转到继承层次结构中的X ,则转换将在运行时成功。

说我们有这个代码:

A a = new A();
B b = (B)a;

这将失败,因为我们需要逆着箭头的方向从AB


你怎么知道演员阵容是否会在编译时失败呢?

这很容易。 只需检查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的具体化理念也很有用。

一些术语和简化是有序的:

  • 当您说“ 类型转换另一种类型”时,前者称为类型,后者称为目标类型。 现在让我们将讨论局限于具体的引用类型(即ABC )。 我们将源类型表示为Source ,将目标类型表示为Target
  • 在涉及转换(隐式或显式)引用类型的赋值语句中,源类型的变量出现在=符号的右侧,而目标类型的变量出现在其左侧。 因此,您可以: Target t = (Target) s ,其中sSource类型的变量。
  • 引用类型的变量有两种类型:编译时类型和运行时类型。

现在, 根据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。)

现在,您的类ABC创建以下层次结构: 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 并且由于CB的子类,因此任何类型C变量(源)都可以始终分配类型B的变量(目标),以使强制转换在运行时成功(即,它不会抛出ClassCastException )。 这被称为Liskov替代原则

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM