我需要在我的一个没有超类的对象中实现深度克隆。

处理检查的最佳方式是什么 CloneNotSupportedException 由超类抛出(即 Object)?

一位同事建议我按以下方式处理:

@Override
public MyObject clone()
{
    MyObject foo;
    try
    {
        foo = (MyObject) super.clone();
    }
    catch (CloneNotSupportedException e)
    {
        throw new Error();
    }

    // Deep clone member fields here

    return foo;
}

这对我来说似乎是一个很好的解决方案,但我想将其扔给 StackOverflow 社区,看看是否还有其他我可以包含的见解。谢谢!

有帮助吗?

解决方案

你绝对必须使用clone?多数人认为Java的clone坏了。

乔希布洛赫上设计 - 复制构造与克隆

  

如果你读过有关克隆在我的书,特别是如果你在字里行间的项目,你就知道我想clone深深打破。 [...]这是一个耻辱,Cloneable被打破,但它发生。

您可以阅读在他的书中的话题更多的讨论的有效的Java第二版,第11项:覆盖clone明智的。他建议,而不是使用拷贝构造函数或复制工厂。

他接着页面写入网页上如何,如果你觉得你一定要,你应该实现clone。但是他这个封闭:

  

时的这一切复杂真的有必要吗?很少。如果要扩展一个类实现Cloneable,你别无选择,只能实现一个乖巧clone方法。否则,您是关闭提供对象拷贝的替代手段,或者干脆不提供能力更好。

的重点是他的,不是我的。


既然你说得很清楚,你别无选择,只能实行clone,这里就是你可以在这种情况下怎么办:确保MyObject extends java.lang.Object implements java.lang.Cloneable。如果是这样的话,那么你就可以保证你将绝不会赶上CloneNotSupportedException。投掷AssertionError一些建议似乎是合理的,但你也可以添加注释,解释了为什么catch块将永远不会进入在这种特殊情况下


另外,正如其他人还建议,可以或许实现clone,而无需调用super.clone

其他提示

有时,它更容易实现拷贝构造函数:

public MyObject (MyObject toClone) {
}

这样可以节省你的处理CloneNotSupportedException麻烦,用final领域工作,你不必对类型回报的担心。

你的代码的工作方式很接近“规范”的方式来写它。我会扔抓内AssertionError,虽然。它用信号通知该行不应该被达成。

catch (CloneNotSupportedException e) {
    throw new AssertionError(e);
}

有两种情况,其中 CloneNotSupportedException 将被抛出:

  1. 被克隆的类未实现 Cloneable (假设实际的克隆最终遵循 Object的克隆方法)。如果您正在编写此方法的类实现了 Cloneable, ,这永远不会发生(因为任何子类都会适当地继承它)。
  2. 异常是由实现显式抛出的 - 这是当超类是时防止子类中的可克隆性的推荐方法 Cloneable.

后一种情况不能发生在您的类中(因为您直接调用超类中的方法) try 块,即使是从子类调用中调用 super.clone())而前者不应该,因为你的班级显然应该实施 Cloneable.

基本上,您应该肯定记录错误,但在这个特定的实例中,只有当您搞乱类的定义时才会发生这种情况。因此将其视为检查版本 NullPointerException (或类似) - 如果您的代码有效,则永远不会抛出该异常。


在其他情况下,您需要为这种可能性做好准备 - 不能保证给定的对象 可克隆,因此当捕获异常时,您应该根据这种情况采取适当的操作(继续使用现有对象,采取替代克隆策略,例如序列化-反序列化,抛出一个 IllegalParameterException 如果你的方法需要通过cloneable等参数。ETC。)。

编辑: :虽然总的来说我应该指出的是, clone() 确实很难正确实现,并且调用者很难知道返回值是否是他们想要的,当您考虑深克隆和浅克隆时更是如此。通常最好完全避免整个事情并使用另一种机制。

使用系列化使深层副本。这不是最快的解决方案,但它不依赖于类型。

您可以实施保护的拷贝构造函数,像这样:

/* This is a protected copy constructor for exclusive use by .clone() */
protected MyObject(MyObject that) {
    this.myFirstMember = that.getMyFirstMember(); //To clone primitive data
    this.mySecondMember = that.getMySecondMember().clone(); //To clone complex objects
    // etc
}

public MyObject clone() {
    return new MyObject(this);
}
public class MyObject implements Cloneable, Serializable{   

    @Override
    @SuppressWarnings(value = "unchecked")
    protected MyObject clone(){
        ObjectOutputStream oos = null;
        ObjectInputStream ois = null;
        try {
            ByteArrayOutputStream bOs = new ByteArrayOutputStream();
            oos = new ObjectOutputStream(bOs);
            oos.writeObject(this);
            ois = new ObjectInputStream(new ByteArrayInputStream(bOs.toByteArray()));
            return  (MyObject)ois.readObject();

        } catch (Exception e) {
            //Some seriouse error :< //
            return null;
        }finally {
            if (oos != null)
                try {
                    oos.close();
                } catch (IOException e) {

                }
            if (ois != null)
                try {
                    ois.close();
                } catch (IOException e) {

                }
        }
    }
}

之多,这里的大部分答案都是有效的,我要告诉您的解决方案也是实际的Java API开发人员是如何做到这一点。 (任乔希布洛赫或尼尔Gafter)

下面是从OpenJDK的提取物,ArrayList类:

public Object clone() {
    try {
        ArrayList<?> v = (ArrayList<?>) super.clone();
        v.elementData = Arrays.copyOf(elementData, size);
        v.modCount = 0;
        return v;
    } catch (CloneNotSupportedException e) {
        // this shouldn't happen, since we are Cloneable
        throw new InternalError(e);
    }
}

正如你已经注意到了和其他人提到,CloneNotSupportedException有,如果你宣布你实现Cloneable界面几乎没有机会抛出。

同时,有没有必要为你覆盖的方法,如果你没有做任何事情在重写方法新。你只需要在你需要做的对象额外的操作来覆盖它,或者你需要把它公开。

最终,它仍是最好的,以避免它和做它使用一些其他方式。

仅仅因为Cloneable的Java的实现是坏,这并不意味着你不能创建你自己的。

如果OP的真正目的是建立一个深克隆,我认为有可能创建这样的接口:

public interface Cloneable<T> {
    public T getClone();
}

然后使用前面提到的原型构造函数来实现:

public class AClass implements Cloneable<AClass> {
    private int value;
    public AClass(int value) {
        this.vaue = value;
    }

    protected AClass(AClass p) {
        this(p.getValue());
    }

    public int getValue() {
        return value;
    }

    public AClass getClone() {
         return new AClass(this);
    }
}

和与ACLASS物场另一个类:

public class BClass implements Cloneable<BClass> {
    private int value;
    private AClass a;

    public BClass(int value, AClass a) {
         this.value = value;
         this.a = a;
    }

    protected BClass(BClass p) {
        this(p.getValue(), p.getA().getClone());
    }

    public int getValue() {
        return value;
    }

    public AClass getA() {
        return a;
    }

    public BClass getClone() {
         return new BClass(this);
    }
}

在这种方式可以easely深克隆类BClass的目的,而不需要@SuppressWarnings或其他花哨代码。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top