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

Java 如何正确覆盖克隆方法?,java,clone,cloning,cloneable,Java,Clone,Cloning,Cloneable,我需要在一个没有超类的对象中实现深度克隆 处理超类(即对象)抛出的选中的CloneNotSupportedException的最佳方法是什么 一位同事建议我按以下方式处理: @Override public MyObject clone() { MyObject foo; try { foo = (MyObject) super.clone(); } catch (CloneNotSupportedException e) {

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

处理超类(即
对象
)抛出的选中的
CloneNotSupportedException
的最佳方法是什么

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

@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社区,看看是否还有其他见解可以包含在内。谢谢

你一定要使用克隆吗?大多数人都同意Java的
clone
已经崩溃了

如果你读过我书中有关克隆的内容,尤其是读过字里行间的内容,你就会知道我认为
clone
已经彻底崩溃了。[…]令人遗憾的是,
Cloneable
被破坏了,但它确实发生了

您可以在他的《有效Java第二版》第11项:明智地覆盖
clone
中阅读更多关于该主题的讨论。他建议改为使用复制构造函数或复制工厂

他接着写了一页又一页,如果你觉得必须的话,你应该如何实现
clone
。但他最后说:

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

重点是他的,不是我的


既然您明确表示除了实现
clone
,您别无选择,那么在这种情况下您可以做些什么:确保
MyObject扩展java.lang.Object实现java.lang.Cloneable
。如果是这种情况,那么您可以保证永远不会捕获
CloneNotSupportedException
。按照一些人的建议抛出
AssertionError
,这似乎是合理的,但您也可以添加一条注释,解释为什么在这种特殊情况下,catch块永远不会被输入



或者,正如其他人所建议的,您可以实现
clone
,而无需调用
super.clone

有时实现副本构造函数更简单:

public MyObject (MyObject toClone) {
}

它省去了处理
CloneNotSupportedException
的麻烦,可以处理
final
字段,您不必担心返回的类型。

用于制作深度副本。这不是最快的解决方案,但它不取决于类型。

有两种情况下会抛出
CloneNotSupportedException

  • 被克隆的类没有实现
    Cloneable
    (假设实际克隆最终遵循
    对象的克隆方法)。如果您在中编写此方法的类实现了
    Cloneable
    ,那么这种情况将永远不会发生(因为任何子类都将适当地继承它)
  • 异常是由一个实现显式抛出的——当超类是可克隆的时,这是防止子类可克隆性的推荐方法
  • 后一种情况不能发生在类中(因为您在
    try
    块中直接调用超类的方法,即使从调用
    super.clone()
    的子类调用也是如此),而前一种情况不应该发生,因为您的类显然应该实现
    Cloneable

    基本上,您应该确定地记录错误,但是在这个特定的实例中,只有当您弄乱了类的定义时,才会发生错误。因此,将其视为选中版本的
    NullPointerException
    (或类似版本)-如果代码正常运行,则永远不会抛出它


    在其他情况下,您需要为这种情况做好准备-无法保证给定对象是可克隆的,因此在捕获异常时,您应该根据这种情况采取适当的操作(继续使用现有对象,采取另一种克隆策略,例如序列化反序列化,如果您的方法需要cloneable提供的参数,则抛出
    非法ParameterException
    ,等等)


    编辑:虽然总的来说我应该指出是的,
    clone()确实很难正确地执行,而且调用方很难知道返回值是否是他们想要的,当你考虑深VS浅克隆时,这样做是加倍的。通常最好避免完全地使用另一种机制。

    你的代码工作方式非常接近“规范”。不过,我会在catch中抛出一个

    AssertionError
    。它表示永远不应该到达那一行

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

    您可以实现受保护的复制构造函数,如下所示:

    /* 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);
    }
    
    公共类MyObject实现可克隆、可序列化{
    @凌驾
    @抑制警告(value=“未选中”)
    受保护的MyObject克隆(){
    ObjectOutputStream oos=null;
    ObjectInputStream ois=null;
    试一试{
    ByteArrayOutputStream bOs=新建ByteArrayOutputStream();
    oos=新对象输出流(bOs);
    oos.writeObject(本);
    ois=newObjectInputStream(newbytearrayinputstream(bOs.toByteArray());
    返回(MyObject)ois.readObject();
    }捕获(例外e){
    //一些严重错误:
    虽然这里的大多数答案都是有效的,但我需要告诉大家,您的解决方案也是实际的Java API开发人员如何做到的
    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) {
    
                    }
            }
        }
    }
    
    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);
        }
    }
    
    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);
        }
    }
    
    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);
        }
    }