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

슈퍼 클래스가없는 내 개체 중 하나에서 깊은 복제를 구현해야합니다.

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하나요? 대부분의 사람들은 자바 clone가 망가 졌다는 데 동의합니다 .

디자인에 대한 Josh Bloch-복사 생성자 대 복제

내 책에서 복제에 대한 항목을 읽었다면, 특히 줄 사이를 읽으면 내가 clone깊이 깨 졌다고 생각한다는 것을 알게 될 것 입니다. […] Cloneable부서진 것이 부끄러운 일이지만 일어납니다.

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

그는 어떻게해야한다고 생각한다면 어떻게 구현해야하는지에 대한 페이지를 썼습니다 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(또는 유사한 버전)처럼 취급하십시오 . 코드가 작동하면 절대로 던지지 않습니다.


다른 상황에서는 이러한 상황에 대비해야합니다. 주어진 객체 복제 가능 하다는 보장이 없으므로 예외를 포착 할 때이 조건에 따라 적절한 조치를 취해야합니다 (기존 객체를 계속 사용하고 대체 복제 전략을 사용하십시오). 예를 들어 serialize-deserialize, IllegalParameterException메서드에 복제 가능 등의 매개 변수가 필요한 경우 throw합니다 .

편집 : 전반적으로 예를 지적해야하지만 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);
}


답변

여기에있는 대부분의 답변이 유효한만큼, 귀하의 솔루션은 실제 Java API 개발자가 수행하는 방식이기도합니다. (Josh Bloch 또는 Neal 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.

또한, 재정의 된 메서드에서 새로운 작업을 수행하지 않으면 메서드를 재정의 할 필요가 없습니다. 객체에 대해 추가 작업을 수행해야하거나 공개로 설정해야하는 경우에만 재정의하면됩니다.

궁극적으로 그것을 피하고 다른 방법을 사용하는 것이 가장 좋습니다.