아이템 13 - clone 재정의는 주의해서 진행하라.
JAVA ·clone 규약
- x.clone() != x 반드시 true
- 즉, 다른 인스턴스
- x.clone().getClass() == x.getClass는 반드시 true
- x.clone().equals(x) true가 아닐 수도 있다.
- 만약 id가 달라진다면 false일 수 있다.
- 불변 객체라면 구현은 다음으로 충분하다.
- 1) Cloneable 인터페이스를 구현하고
- 이 인터페이스는 Object의 protected 메서드인 clone의 동작 방식을 결정한다.
- Cloneable을 구현한 클래스에서 clone을 호출하면 객체의 필드들을 하나하나 복사한 객체를 반환.
- 그렇지 않은 클래스에서 clone을 호출하면 CloneNotSupportedException을 던진다.
- 2) clone 메서드를 재정의한다. 이때 super.clone()을 사용해야 한다.
- super.clone을 호출하지 않는다면 Cloneable을 구현할 이유가 없다. </br> </br>
- 1) Cloneable 인터페이스를 구현하고
가변 객체의 clone 구현하는 방법
- 접근 제한자는 public, 반환 타입은 자신의 클래스로 변경한다.
- super.clone을 호출한 뒤 필요한 필드를 적절히 수정한다.
- 배열을 복제할 때는 배열의 clone 메서드를 사용하라.
- 경우에 따라 final을 사용할 수 없을지도 모른다.
- 필요한 경우 deep copy를 해야한다.
- 얕은 복사를 하면 복사본 수정시 원본도 수정될 수 있다.
- super.clone으로 객체를 만든 뒤, 고수준 메서드 호출하는 방법도 있다.
- 이렇게 고수준 API를 활용해 복제하면 간단하고 보기 좋은 코드를 얻지만, 저수준보다 느리다.
- 오버라이딩 할 수 있는 메서드는 참조하지 않도록 조심해야 한다.
- 상속용 클래스는 Cloneable을 구현하지 않는 것이 좋다.
- Cloneable을 구현한 스레드 안전 클래스를 작성할 때는 동기화(synchronized)를 해야 한다. </br> </br>
- 배열을 복제할 때는 배열의 clone 메서드를 사용하라.
clone 대안
- “복사 생성자” 또는 변환 생성자, “복사 팩터리” 또는 변환 팩터리 사용하기
- 생성자를 쓰지 않으며, 모호한 규약, 불필요한 검사 예외, final 용법 방해 등에서 벗어날 수 있다.
- 또 다른 큰 장점 중 하나로 인터페이스 타입의 인스턴스를 인수로 받을 수 있다.
- 클라이언트가 복제본의 타입을 결정할 수 있다. </br> </br>
정리
Cloneable은 문제가 많다. 그래서 새로운 인터페이스를 만들 때는 절대 Cloneable을 확장해서는 안되며, 새로운 클래스도 이를 구현해서는 안된다.
final 클래스라면 Cloneable을 구현해도 위험이 크지 않지만, 성능 최적화 관점에서 검토한 후 별다른 문제가 없을 때만 드물게 허용해야 한다.
기본 원칙은 ‘복제 기능’은 생성자와 팩터리를 이용하는게 최고다. 단, 배열만은 clone 메서드 방식이 가장 깔끔한, 이 규칙의 합당한 예외이다.