아이템 11. clone을 재정의할 때는 신중하라.

11. clone을 재정의할 때는 신중하라.

java.lang.Object명세(JavaSE6) clone 메서드의 일반 규약

명세 3과 같은 이유로 인해, 비 final 클래스에 clone을 재정의 할때는 반드시 super.clone을 호출해서 얻은 객체를 반환해야 한다.

@Override
public PhoneNumber clone(){
	try{
		return (PhoneNumber) super.clone();
	}catch( CloneNotSupportedException e ){
		throw new AssertionError(); // JavaSE 1.5 이후에선 수행될 일 없음
	}
}

위의 메서드는 Object가 아니라 PhoneNumber 객체를 반환한다. 1.5 버전 이후 부터 공변 반환형(convariant return type)이 도입되었기 때문이다.

Stack 클래스와 같이 내부 구조가 복잡한 클래스에 clone를 구현하려면 다음과 같이 구현되어야 한다.

@Override
public Stack clone(){
	try{
		Stack stack = (Stack) super.clone(); // Stack의 껍대기 구조만 복사함
		stack.elements = elements.clone(); // 내부 데이터들도 복제해야 함( 주의, elements가 final로 선언되어 있으면 동작하지 않음)
		return stack;
	}catch( CloneNotSupportedException e ){
		throw new AssertionError();
	}
}

사실상, clone메서드는 또 다른 형태의 생성자다. 원래 객체를 손상시키는 일이 없도록 해야 하고, 복사본의 불변식(invariant)도 제대로 만족시켜야 한다.

복사해야 하는 데이터가 점점 복잡해 질수록 clone 메서드에서 고려해야 될 사항이 아주 많아진다.(Effective Java 2/E 77.p 참조)

결국, 객체 복제를 지원하는 좋은 방법은, 복사 생성자(copy constructor)나 복사 팩터리(copy factory)를 제공하는 것이다.