Java 如何正确覆盖克隆方法?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2326758/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-13 06:11:26  来源:igfitidea点击:

How to properly override clone method?

javaclonecloningcloneable

提问by Cuga

I need to implement a deep clone in one of my objects which has no superclass.

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

What is the best way to handle the checked CloneNotSupportedExceptionthrown by the superclass (which is Object)?

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

A coworker advised me to handle it the following way:

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

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

    // Deep clone member fields here

    return foo;
}

This seems like a good solution to me, but I wanted to throw it out to the StackOverflow community to see if there are any other insights I can include. Thanks!

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

采纳答案by polygenelubricants

Do you absolutely have to use clone? Most people agree that Java's cloneis broken.

你绝对必须使用clone吗?大多数人都同意 Javaclone已损坏。

Josh Bloch on Design - Copy Constructor versus Cloning

Josh Bloch 谈设计 - 复制构造函数与克隆

If you've read the item about cloning in my book, especially if you read between the lines, you will know that I think cloneis deeply broken. [...] It's a shame that Cloneableis broken, but it happens.

如果你读过我书中关于克隆的文章,特别是如果你在字里行间读过,你就会知道我认为它clone被深深地打破了。[...] 很遗憾Cloneable被打破了,但它发生了。

You may read more discussion on the topic in his book Effective Java 2nd Edition, Item 11: Override clonejudiciously. He recommends instead to use a copy constructor or copy factory.

您可以在他的《Effective Java 2nd Edition, Item 11: Override clonejudiciously 》一书中阅读有关该主题的更多讨论。他建议改为使用复制构造函数或复制工厂。

He went on to write pages of pages on how, if you feel you must, you should implement clone. But he closed with this:

他接着写了几页关于如何(如果您认为必须)实施clone. 但他以这样的方式结束:

Is all this complexities really necessary? Rarely. If you extend a class that implements Cloneable, you have little choice but to implement a well-behaved clonemethod. Otherwise, you are better off providing alternative means of object copying, or simply not providing the capability.

所有这些复杂性真的有必要吗?很少。如果你扩展一个实现 的类Cloneable,你别无选择,只能实现一个行为良好的clone方法。否则,您最好提供对象复制的替代方法,或者干脆不提供功能

The emphasis was his, not mine.

重点是他的,不是我的。



Since you made it clear that you have little choice but to implement clone, here's what you can do in this case: make sure that MyObject extends java.lang.Object implements java.lang.Cloneable. If that's the case, then you can guarantee that you will NEVERcatch a CloneNotSupportedException. Throwing AssertionErroras some have suggested seems reasonable, but you can also add a comment that explains why the catch block will never be entered in this particular case.

由于您明确表示除了实施之外别无选择clone,因此在这种情况下您可以执行以下操作:确保MyObject extends java.lang.Object implements java.lang.Cloneable. 如果是这种情况,那么您可以保证永远不会捕获CloneNotSupportedException. AssertionError正如一些人所建议的那样抛出似乎是合理的,但您也可以添加一个注释来解释为什么在这种特殊情况下永远不会输入 catch 块。



Alternatively, as others have also suggested, you can perhaps implement clonewithout calling super.clone.

或者,正如其他人所建议的那样,您也许可以在clone不调用super.clone.

回答by Aaron Digulla

Sometimes it's more simple to implement a copy constructor:

有时实现复制构造函数更简单:

public MyObject (MyObject toClone) {
}

It saves you the trouble of handling CloneNotSupportedException, works with finalfields and you don't have to worry about the type to return.

它为您省去了处理CloneNotSupportedException、处理final字段的麻烦,而且您不必担心要返回的类型。

回答by Micha? Ziober

Use serializationto make deep copies. This is not the quickest solution but it does not depend on the type.

使用序列化来制作深拷贝。这不是最快的解决方案,但它不取决于类型。

回答by Andrzej Doyle

There are two cases in which the CloneNotSupportedExceptionwill be thrown:

有两种情况CloneNotSupportedException会抛出 will:

  1. The class being cloned does not implemented Cloneable(assuming that the actual cloning eventually defers to Object's clone method). If the class you are writing this method in implements Cloneable, this will never happen (since any sub-classes will inherit it appropriately).
  2. The exception is explicitly thrown by an implementation - this is the recommended way to prevent clonability in a subclass when the superclass is Cloneable.
  1. 被克隆的类没有实现Cloneable(假设实际的克隆最终遵循Object的 clone 方法)。如果您在实现中编写此方法的类Cloneable,这将永远不会发生(因为任何子类都会适当地继承它)。
  2. 异常由实现显式抛出 - 当超类为Cloneable.

The latter case cannot occur in your class (as you're directly calling the superclass' method in the tryblock, even if invoked from a subclass calling super.clone()) and the former should not since your class clearly should implement Cloneable.

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

Basically, you should log the error for sure, but in this particular instance it will only happen if you mess up your class' definition. Thus treat it like a checked version of NullPointerException(or similar) - it will never be thrown if your code is functional.

基本上,您应该肯定地记录错误,但在这种特殊情况下,只有当您弄乱类的定义时才会发生这种情况。因此,将其视为NullPointerException(或类似)的检查版本- 如果您的代码正常运行,它将永远不会被抛出。



In other situations you would need to be prepared for this eventuality - there is no guarantee that a given object iscloneable, so when catching the exception you should take appropriate action depending on this condition (continue with the existing object, take an alternative cloning strategy e.g. serialize-deserialize, throw an IllegalParameterExceptionif your method requires the parameter by cloneable, etc. etc.).

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

Edit: Though overall I should point out that yes, clone()really is difficult to implement correctly and difficult for callers to know whether the return value will be what they want, doubly so when you consider deep vs shallow clones. It's often better just to avoid the whole thing entirely and use another mechanism.

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

回答by Chris Jester-Young

The way your code works is pretty close to the "canonical" way to write it. I'd throw an AssertionErrorwithin the catch, though. It signals that that line should never be reached.

您的代码的工作方式与编写它的“规范”方式非常接近。不过,我会AssertionError在捕获范围内扔一个。它表示永远不应该到达那条线。

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

回答by Dolph

You can implement protected copy constructors like so:

您可以像这样实现受保护的复制构造函数:

/* 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);
}

回答by Kvant_hv

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) {

                }
        }
    }
}

回答by Haggra

As much as the most of the answers here are valid, I need to tell that your solution is also how the actual Java API developers do it. (Either Josh Bloch or Neal Gafter)

尽管这里的大多数答案都是有效的,但我需要说明的是,您的解决方案也是实际 Java API 开发人员的做法。(乔什·布洛赫或尼尔·加夫特)

Here is an extract from openJDK, ArrayList class:

这是来自 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);
    }
}

As you have noticed and others mentioned, CloneNotSupportedExceptionhas almost no chance to be thrown if you declared that you implement the Cloneableinterface.

正如你所注意到的和其他人提到的,CloneNotSupportedException如果你声明你实现了Cloneable接口,几乎没有机会被抛出。

Also,there is no need for you to override the method if you don't do anything new in the overridden method. You only need to override it when you need to do extra operations on the object or you need to make it public.

此外,如果您不在被覆盖的方法中执行任何新操作,则无需覆盖该方法。只有在需要对对象进行额外操作或需要将其公开时才需要覆盖它。

Ultimately, it is still best to avoid it and do it using some other way.

最终,最好还是避免它并使用其他方式来做到这一点。

回答by Francesco Rogo

Just because java's implementation of Cloneable is broken it doesn't mean you can't create one of your own.

仅仅因为 java 的 Cloneable 实现被破坏并不意味着您不能创建自己的实现。

If OP real purpose was to create a deep clone, i think that it is possible to create an interface like this:

如果 OP 的真正目的是创建深度克隆,我认为可以创建这样的界面:

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

then use the prototype constructor mentioned before to implement it:

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

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);
    }
}

and another class with an AClass object field:

和另一个具有 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);
    }
}

In this way you can easely deep clone an object of class BClass without need for @SuppressWarnings or other gimmicky code.

通过这种方式,您可以轻松地深度克隆 BClass 类的对象,而无需 @SuppressWarnings 或其他花哨的代码。