为什么#clone()不在Cloneable接口中?

时间:2012-12-02 14:22:17

标签: java

我正在阅读正确执行数组的深层副本,但是我对#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以这种方式完成它?我确信他们有合理的理由,但我似乎无法弄明白。

3 个答案:

答案 0 :(得分:20)

Java中的克隆合同规定每个clone实现必须首先从super.clone()获取克隆的实例。这将创建一个始终以Object.clone调用结束的链,并且该方法包含“神奇的”本机级代码,该代码生成表示Java对象的基础原始struct的二进制副本。如果这个机制不存在,clone就不会是多态的:Object.clone方法会生成一个被调用的类的实例;没有本机代码就无法再现。

这就是为什么Object.clone方法无法避免的原因。 Cloneable 可能包含clone方法,但会产生有关throws子句的问题。它的表达方式可以自由声明clone没有声明的异常,或声明任意异常。如果方法已在界面中声明,则无法实现这种灵活性。

请记住,Generics对于克隆几乎没用:在protected T clone()中想象ObjectT来自哪里?我们是否需要Object<T>并强制 Java Universe 中的每一个类进行参数化,而这一切只是为了使这个半弃用的机制更好地工作?请记住,此代码完全合法:

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

你可以叫它:

TheMightyOne one = new TheMightyOne();
TheMightyOne two = one.clone(); // do downcasts needed

答案 1 :(得分:4)

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

克隆是在仿制药之前定义的。

答案 2 :(得分:0)

clone()方法是不必要的,每个对象都可以通过反射来调用Object.clone方法,因此克隆对象取决于是否实现Cloneable接口。这是出于安全原因。您可以简单地克隆一个通过此util实现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;
}