18

我正在阅读正确执行数组的深拷贝,但是我对如何#clone()实现感到困惑。它是java.lang.Object该类的成员,但是如果您阅读 javadocs:

首先,如果该对象的类没有实现接口 Cloneable,则抛出 CloneNotSupportedException。

那么为什么clone首先在那里定义方法呢?当然,如果一个方法只能在接口存在时使用,你应该把方法放在接口中。Cloneable接口本身是空的;它只是Java用来确保使用该clone方法是合法的标记接口。

这样做也消除了使用泛型来确保类型安全的能力:

class Foo implements Cloneable { // Valid.
    @Override
    public Object clone() throws CloneNotSupportedException {
        // ...
    }
}

class TypeSafeFoo implements Cloneable<TypeSafeFoo> { // Not valid.
    @Override
    public TypeSafeFoo clone() throws CloneNotSupportedException {
        // ...
    }
}

为什么 Java 会这样做呢?我确信他们有正当的理由,但我似乎无法弄清楚。

4

3 回答 3

27

Java 中的克隆契约规定每个clone实现必须首先从super.clone(). 这将创建一个始终以调用 结束的链Object.clone,并且该方法包含“神奇的”本机级代码,该代码生成struct表示 Java 对象的底层 raw 的二进制副本。如果这种机制不存在,clone那么它就不能是多态的:该Object.clone方法会生成调用它的任何类的实例;没有本机代码,这是无法复制的。

这就是为什么Object.clone无法避免这种方法的原因。Cloneable 可以包含一个clone方法,但它会产生有关该throws子句的问题。就目前的情况而言,您可以自由声明clone不声明异常,或声明任意异常。如果方法已在接口中声明,则无法实现这种灵活性。

请记住,泛型对于克隆几乎没有用处:想象一下protected T clone()Object从哪里来T?我们是否需要Object<T>并强制Java 世界中的每个类都对其自身进行参数化,而这一切只是为了让这种半弃用的机制更好地工作吗?还要记住,这段代码是完全合法的:

public class TheMightyOne implements Cloneable {
   @Override public TheMightyOne clone() {
     return (TheMightyOne) super.clone();
   }
}

你可以这样称呼它:

TheMightyOne one = new TheMightyOne();
TheMightyOne two = one.clone(); // do downcasts needed
于 2012-12-02T15:23:42.600 回答
4

为了处理创建克隆和基本字段复制,克隆需要继承一个方法实现。Cloneable 类可以出现在层次结构中的任何位置,并且可能需要扩展特定的超类来完成其主要工作。所有可能的 Cloneable 类都可以从其继承实现的唯一超类是 Object。

克隆早在泛型之前就已经定义了。

于 2012-12-02T14:31:13.257 回答
0

clone()方法是不必要的,每个对象都可以Object.clone通过反射来调用方法,所以一个对象是否可以克隆取决于是否实现Cloneable接口。这是出于安全原因。你可以简单地克隆一个由这个工具实现的对象Cloneable

@SuppressWarnings("unchecked")
public static <T extends Cloneable> T clone(Cloneable obj) {
    T rtn = null;
    try {
        Method method = Object.class.getDeclaredMethod("clone");
        method.setAccessible(true);
        rtn = (T) method.invoke(obj);
    } catch (Throwable t) {
        t.printStackTrace();
    }
    return rtn;
}
于 2013-06-27T09:10:52.363 回答