Item 11. clone을 재정의할 때는 신중하라
Clonable
clone 허용을 알리는데 쓰려고 고안된 mixin interface. but clone 메서드는 존재하지 않는다 + Object의 clone 메서드는 protected => 즉, Cloneable을 구현해도 clone 메서드를 호출할 순 없다 (병맛)
대신 Object의 protected clone 메서드가 어떻게 동작할지를 정의한다.
어떤 class가 Cloneable을 구현하면, Object의 clone 메서드에서 해당 객체를 필드 단위로 복사한 객체를 반환.
Cloneable을 구현하지 않은 클래스의 clone 메서드를 사용시, Exception이 떨어짐.
=> Object.clone() 메서드 호출시, 해당 class가 Cloneable을 구현했는지 확인하고 아니면 CloneNotSupportedExcepiton 을 던지는 것임
인터페이스를 굉장히 이상하게 사용(상위 클래스의 protected 멤버가 어떻게 동작할지 규정)하는 예로 이렇게 쓰면 안된다
public interface Cloneable {
// 비 어 있 다
}
public class Object {
...
protected native Object clone() throws CloneNotSupportedException;
}
class Test impletments Cloneable (extends Object) {
}
Object.clone 메서드의 규약
- x.clone() != x : always true
- x.clone().getClass() == x.getClass() : not an absolute requirement
- x.clone().equals(x) : not an absolute requirement
뭐...뭔소릴 하는겨?
제대로 동작하는 clone 메서드를 구현하는 방법
Cloneable을 구현한 모든 class는, return type이 자기 자신인 public clone 메서드를 구현해야 한다
- public clone 메서드 내부
- 맨 처음 super.clone() 호출
- 위에서 만들어진 객체에서, 수정해야하는 필드 수정
- 반환할 객체의 type을 Cloneable을 구현한 class에 맞춘다
- ex. PhoneNumber
public PhoneNumber implements Cloneable { @Override public PhoneNumber clone() [throw CloneNotSupportedException] { return (PhoneNumber) super.clone(); } }
- 라이브러리가 할 일을 클라이언트에게 미루지 마라
- ex. PhoneNumber
- 내부 복제일 때는 clone을 재귀적으로 호출
- ex. Stack
public Stack implements Cloneable { @Override public Stack clone() [throw CloneNotSupportedException] { Stack result = (Stack) super.clone(); result.elements = elements.clone(); return result; } }
- clone 메서드는 또 다른 형태의 생성자. 원래 객체를 손상시키면 안된다.
- 만약 elements 필드가 final이면 불가능. => final 선언을 지워야 할 수도 있음
- ex. Stack
- 내부 복제 재귀의 재귀,,, 재귀랄
- ex. HashTable
- deepCopy 이용 : 내부 필드를 다시 복제하도록
- 모든 필드를 초기상태로 돌리고, 상위 레벨 메서드를 다시 호출(ex. setter)하여 객체 상태를 다시 만듬
- 간단 및 우아..하지만 빠르진 않음
- 반환할 객체의 type을 Cloneable을 구현한 class에 맞춘다
- 필드가 전부 기본 자료형이거나 immutable이면 필드를 수정할 필요가 없음
- throw CloneNotSupportedException은 생략
- 컴파일 시 예외 처리 여부를 검사를 강요하는(checked exception) 메서드는 사용하기 불편
- but, 클래스가 계승을 위해 설계된 부모 클래스일 경우엔 Object.clone을 따라, protected/throw CloneNotSupportedException/Cloneable implements x 를 모두 충족해야함
- 그래야 하위 클래스에서 Cloneable을 implements 할 수 있겠지
clone 메서드를 정의하면 좋은 경우
clone 메서드의 대안
- copy constructor
- 같은 클래스의 객체를 paramter로 받는 생성자
public Yum(Yum yum);
- 같은 클래스의 객체를 paramter로 받는 생성자
- copy factory method
- 같은 클래스의 객체를 paramter로 받는 static factory 메서드
public static Yum newInstance(Yum yum);
- 같은 클래스의 객체를 paramter로 받는 static factory 메서드
장점은 Cloneable/clone이 가진 문제점을 안가진다는 것이겠지 ....