[Effective Java 3/e] 아이템 13 - clone 재정의는 주의해서 진행하라
아이템 13 - clone 재정의는 주의해서 진행하라
가변 상태를 참조하지 않는 클래스용 clone 메서드
@Override public PhoneNumber clone() {
try{
return (PhoneNumber) super.clone();
} catch (CloneNotSupportedException e) {
throw new AssertionError(); // 일어날 수 없는 일이다.
}
}
- 클라이언트가 형변환하지 않아도 되게 함.
- try-catch 블록으로 감싼 이유
- checked exception 인 CloneNotSupportedException 을 unchecked exception 으로 변한하기 위함
가변 객체
public class Stack {
private Object[] elements;
private int size = 0;
private static final int DEFAULT_INITIAL_CAPACITY = 16;
public Stack() {
this.elements = new Object[DEFAULT_INITIAL_CAPACITY];
}
public void push(Object e) {
ensureCapacity();
elements[size++] = e;
}
public Object pop() {
if (size ==0)
throw new EmptyStackException();
Object result = elements[--size];
elements[size] = null; // 다 쓴 참조 해제
return result;
}
... // 나머지 코드는 생략
}
- clone 메서드가 단순히 super.clone 결과를 그대로 반환 할 경우
- Stack 인스턴스의 size 필드는 올바른 값 가짐
- elements 필드는 원본 Stack 인스턴스와 똑같은 배열 참조
- 원본이나 복제본 중 하나를 수정하면 같이 수정되어 불변식을 해침
가변 상태를 참조하는 클래스용 clone 메서드
@Override public Stack clone() {
try{
Stack result = (Stack) super.clone();
result.elements = elements.clone();
return result;
} catch (CloneNotSupportedException e) {
throw new AssertionError(); // 일어날 수 없는 일이다.
}
}
- 배열의 clone은 런타임 타입과 컴파일타입 모두가 원본 배열과 똑같은 배열을 반환한다.
- 배열을 복제할 때는 배열의 clone 메서드 사용을 권장
clone 메서드
- clone 메서드는 생성자와 같은 효과를 낸다.
- clone은 원본 객체에 아무런 해를 끼치지 않는 동시에 복제된 객체의 불변식을 보장해야 한다.
- Cloneable을 이미 구현한 클래스를 확장한다면 clone을 잘 작동하도록 구현해야 한다.
- 그렇지 않은 상황에서는 복사 생성자와 복사 팩터리라는 더 나은 객체 방식을 제공할 수 있다.
복사 생성자
public Yum(Yum yum) { ... };
복사 팩터리
public static Yum newInstance(Yum yum) { ... };
- 복사 팩터리는 복사 생성자를 모방한 정적 팩터리다.
복사 생성자와 복사 팩터리가 Cloneable/clone 방식보다 나은점
- 언어 모순적이고 위험한 객체 생성 매커니즘(생성자를 쓰지 않는 방식)을 사용하지 않는다.
- 정상적인 final 필드 용법과 충돌하지 않는다.
- 불필요한 검사 예외를 던지지 않는다.
- 형변환이 필요하지 않다.
- 해당 클래스가 구현한 ‘인터페이스’ 타입의 인스턴스를 인수로 받을 수 있다.
정리
- 새로운 인터페이스를 만들 때는 절대 Cloneable을 확장해서는 안 되며, 새로운 클래스도 이를 구현해서는 안 된다.
- final 클래스라면 Cloneable을 구현해도 위험이 크지 않지만, 성능 최적화 관점에서 검토한 후 문제가 없을 때만 허용해야 한다.
- 복제 기능은 생성자와 팩터리를 이용하는게 최고!
- 단, 배열만은 clone 메서드 방식이 가장 깔끔함