Java - 克隆中的向下转型

7
假设 Class B 继承自 class A,而 class A 是可克隆的,代码如下:
public class A implements Cloneable {
    public Object clone() throws CloneNotSupportedException {
        A ac = (A) super.clone();
        return ac;
    }
}

public class B extends A {
    public Object clone() throws CloneNotSupportedException {
        B a = (B) super.clone();
        return a;
    }
}

为什么在下一行执行从A到B的向下转换(down-cast)是合法的:

B a = (B) super.clone(); // (super of B is A)

当进行下行转换时,出现运行时错误?

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

提前感谢您!


你是不是指 B b = new B();A a = (A) b.clone(); 这两行代码? - blalasaadri
关于clone()的问题非常好,我也曾经无法理解。 - raiks
1个回答

10
最终,这是使用Object.clone()来创建对象的 - 这保证了创建一个与调用它的对象相同执行时类型的新对象:

Object的方法clone执行特定的克隆操作。首先,如果此对象的类没有实现接口Cloneable,则抛出CloneNotSupportedException。请注意,所有数组都被认为实现了接口Cloneable,而数组类型T []的克隆方法的返回类型为T [],其中T是任何引用或原始类型。否则,此方法将创建此对象的类的新实例,并使用与该对象的相应字段的内容完全相同的内容初始化其所有字段,就像通过赋值一样;字段的内容本身不会被克隆。因此,此方法执行此对象的“浅层复制”,而不是“深层复制”操作。

所以,如果我们在B实例上执行clone()的调用,则super.clone()将返回B(或子类)- 因此转换是有效的。
在第二种情况下,您正在创建仅A的实例,这不是B的实例,因此转换失败。

这就是为什么克隆方法的实现绝对不应该直接通过new来创建副本,而应该始终使用super.clone()!否则,在子类中正确地重写克隆方法将变得不可能! - isnot2bad
谢谢你详细的回答。当我调试代码时,下一行代码: B a = (B) super.clone(); 把我带到了 A.clone(),它返回 A 类的实例。所以我不明白为什么 super.clone() 会返回一个 B。 - Hesham Yassin
2
@HeshamYassin:不,A.clone()返回的是至少一个 A 的引用 - 这是由于向 A 的强制转换所保证的。但实际上它将是一个 B(或子类),因为你正在克隆一个 B 的实例。在调试器中查看 ac 的值,你会发现它是一个指向 B 实例的引用。 - Jon Skeet

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接