Programing

복제 방법을 올바르게 재정의하는 방법은 무엇입니까?

crosscheck 2020. 8. 3. 17:42
반응형

복제 방법을 올바르게 재정의하는 방법은 무엇입니까?


수퍼 클래스가없는 객체 중 하나에서 딥 클론을 구현해야합니다.

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가 깨 졌다는 데 동의합니다 .

디자인에 관한 Josh Bloch-복제 생성자와 복제

내 책에서 복제에 대한 항목을 읽은 경우, 특히 줄 사이를 읽으면 내가 clone크게 망가 졌다고 생각 합니다. [...] 그것은 부끄러운 Cloneable일이지만 일어납니다.

그의 책 Effective Java 2nd Edition, Item 11 : Overrideclone in 신중하게 주제에서 더 많은 토론을 읽을 수 있습니다 . 대신 복사 생성자 또는 복사 팩토리를 사용하는 것이 좋습니다.

그는 당신이 어떻게해야한다고 생각하는지 어떻게 구현해야하는지에 대한 페이지 페이지를 작성했습니다 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의 복제 방법을 연기한다고 가정 ). 이 메소드를 작성하는 클래스가 implements Cloneable에서는 서브 클래스가 적절하게 상속하기 때문에 이런 일이 발생하지 않습니다.
  2. 예외는 구현에 의해 명시 적으로 발생합니다. 이것은 수퍼 클래스가있을 때 서브 클래스에서 복제 성을 방지하기 위해 권장되는 방법입니다 Cloneable.

후자의 경우는 클래스에서 발생할 수 없으며 ( try하위 클래스 호출에서 호출 된 경우에도 블록 에서 수퍼 클래스 '메서드를 직접 호출하므로) 클래스 super.clone()는 클래스를 명확하게 구현해야하므로해서는 안됩니다 Cloneable.

기본적으로 오류를 확실히 기록해야하지만이 특별한 경우 클래스 정의를 엉망으로 만드는 경우에만 발생합니다. 따라서 확인 된 버전 NullPointerException(또는 유사한)으로 취급하십시오 -코드가 작동하는 경우 절대로 throw되지 않습니다.


In other situations you would need to be prepared for this eventuality - there is no guarantee that a given object is cloneable, 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 IllegalParameterException if your method requires the parameter by cloneable, etc. etc.).

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.


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


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

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

                }
        }
    }
}

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)

Here is an extract from openJDK, ArrayList class:

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, CloneNotSupportedException has almost no chance to be thrown if you declared that you implement the Cloneable interface.

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.


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

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

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:

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.

참고URL : https://stackoverflow.com/questions/2326758/how-to-properly-override-clone-method

반응형