backend/Java

[Effective Java] 11. clone 메소드는 신중하게 오버라이드 하자.

버리야 2009. 10. 15. 10:03
반응형
[Effective Java] 11. clone 메소드는 신중하게 오버라이드 하자.


Cloneable 인터페이스는 복제를 허용하는 객체라는 것을 알리는 목적으로 사용하는 믹스인 인터페이스(mixin interface)

아무런 method도 없는 Cloneable 인터페이스는 어디에 쓰이는 것일까?

이 인터페이스는 Object 클래스의 protected 메소드인 clone의 행동 방식을 규정한다. 만약, clone 메소드가 호출된 객체가 Cloneable 타입이라면, Object.clone 메소드는 이 객체의 모든 필드를 그대로 복사한 복제본을 리턴한다. 하지만 Cloneable 타입이 아니라면 CloneNotSupportedException을 던진다.

java.lang.Object.clone의 명세(Specification)

this 객체의 복제본을 생성하여 리턴한다. "복제본"의 정확한 의미는 this 객체의 클래스마다 달라질 수 있다. 

보통 모든 객체 x에 대하여, 다음 식
x.clone() != x
는 true 이고, 또 다음 식
x.clone().getClass() == x.getClass()

도 true이지만 반드시 지켜야 할 사항은 아니다. 또 다음 식

x.clone().equals(x)

도 true이지만 반드시 지켜야 할 사항은 아니다. 어떤 객체를 복제할 때 보통 이 객체 클래스의 새로운 인스턴스는 반드시 생성해야 하지만, 내부 데이터 구조도 역시 복제해야 할지도 모른다. 이 메소드는 어떤 생성자도 호출하면 안 된다.


만일 어떤 클래스로부터 상속받는 서브 클래스를 만들고 그 서브 클래스에서 super.clone을 호출하면, 서브 클래스의 인스턴트가 복제되어 반환될거라고 생각할 것이다.
이 경우 슈퍼 클래스에서 그렇게 할 수 있는 유일한 방법은, 자신의 clone 메소드에서 super.clone을 호출하여 얻은 객체를 반환하는 것이다.

만일, 슈퍼 클래스의 clone 메소드에서 또 다시 super.clone을 호출하지 않고 생성자를 호출하여 생성된 객체를 반환한다면 잘못된 클래스(타입)의 객체가 될 것이다.

그러므로 final이 아닌 수퍼 클래스의 clone 메소드를 오버라이드 할 경우, 서브 클래스의 clone에서 반드시 super.clone을 호출하여 얻은 객체를 반환해야 한다.



일반적인 Class의 clone() method
- field가 모두 기본형 타입일 경우

public Object clone() {
        try {
                return super.clone();
        } catch(CloneNotSupportedException e) {
                throw new Error("Assertion failuer");
        }
}



어떤 객체가 가변 객체를 참조하는 필드를 갖고 있는 Stack클래스를 생각해 보자.

Stack 클래스의 clone 메소드가 올바르게 동작하려면, 원본 객체가 내부적으로 포함하고 있는 객체까지도 복제해주어야 한다.

그리고 그렇게 하는 가장 쉬운 방법은, elements 배열에 대해 재귀적으로 clone 메소드를 호출하는 것이다.

Cloneable을 implements 하는 클래스의 서브 클래스에서는 clone 메소드를 잘 구현해야한다.

객체를 복제하는 다른 방법을 제공하거나, 또는 복제할 수 없도록 하는 것이 좋다.

예를 들어, 불변 클래스의 객체 복제를 지원하는 것은 바람직하지 않다. 사실상 복제본이 원본과 같기 때문이다.


객체를 복제하는 좋은 방법은 복제 생성자나 복제 팩토리 메소드를 제공하는 것이다. 복제 생성자는 그냥 생성자로써, 그 생성자를 포함하는 클래스를 타입으로 하는

인자를 하나만 갖는다. 


public Yum(Yum yum);


복제 팩토리는 복제 생성자와 유사한 static 팩토리 메소드로 다음과 같다.


public static Yum newInstance(Yum yum);


복제 생성자와 복제 팩토리 메소드를 사용하는 방법은 Cloneable 과 Clone을 사용하는 것에 대해 많은 장점을 갖고 있다.


반응형