Posts [이펙티브자바] 아이템13-clone 재정의는 주의해서 진행하라
Post
Cancel

[이펙티브자바] 아이템13-clone 재정의는 주의해서 진행하라

Cloneable 인터페이스를 구현하는 모든 클래스는 clone을 재정의해야 한다

  • 이때 접근 제한자는 public으로, 반환 타입은 클래스 자신으로 변경한다.
  • 이 메서드는 가장 먼저 super.clone을 호출한 후 필요한 필드를 전부 적절히 수정한다.
  • 일반적으로 이 말은 그 객체의 내부 깊은 구조에 숨어 있는 모든 가변 객체를 복사하고, 복제본이 가진 객체 참조 모두가 복사된 객체들을 가리키게 함을 뜻한다.
  • 이러한 내부 복사는 주로 clone을 재귀적으로 호출해 구현하지만, 이 방식이 항상 최선인 것은 아니다.
  • 기본 타입 필드와 불변 객체 참조만 갖는 클래스라면 아무 필드도 수정할 필요가 없다.
    • 단, 일련번호나 고유 ID는 비록 기본 타입이나 immutable일지라도 수정해줘야 한다.

위의 모든 작업이 꼭 필요할까?

  • 다행히도 이처럼 복잡한 경우는 드물다.
  • cloneable을 이미 구현한 클래스를 확장한다면 어쩔 수 없이 clone을 잘 작동하도록 구현해줘야 한다.
  • 그렇지 않은 상황에서 복사 생성자와 복사 패터리라는 더 나은 객체 복사 방식을 제공할 수 있다.
  • 복사 생성자란 단순히 자신과 같은 클래스의 인스턴스를 인수로 받는 생성자를 말한다.

복사 생성자

1
public Yum(Yum yum){...};

복사 팩터리

1
public static Yum newInstance(Yum yum){...};
  • 복사 생성자와 그 변형인 복사 팩터리는 cloneable/clone 방식보다 나은 면이 많다.
  • 언어 모순적이고 위험천만한 객체 생성 메커니즘(생성자를 쓰지 않는 방식)을 사용하지 않으며, 엉성하게 문서화된 규약에 기대지 않고, 정상적인 final 필드 용법과도 충돌하지 않으며, 불필요한 검사 예외를 던지지도 않고, 형변환도 필요하지 않다.

  • 또한 해당 클래스가 구현한 ‘인터페이스’타입의 인스턴스를 인수로 받을 수 있다.
  • 예컨대 관례상 모든 범용 컬렉션 구현체는 Collection이나 Map 타입을 받는 생성자를 제공한다.

  • 인터페이스 기반 복사 생성자와 복사 팩터리의 더 정확한 이름은 변환 생성자(converstion constructor), 변환 팩터리(conversion factory)다.
  • 이들을 활용하면 클라이언트는 원본의 구현 타입에 얽매이지 않고 복제본의 타입을 직접 선택할 수 있다.
    • 예를 들어 HashSet 객체 s를 TreeSet 타입으로 복제할 수 있다. clone으로는 불가능한 이 기능을 간단히 new TreeSet<>(s)로 처리 할 수 있다.

핵심 정리: Cloneable이 몰고 온 모든 문제를 되짚어봤을 때, 새로운 인터페이스를 만들 때는 절대 Cloneable을 확장해서는 안 되며, 새로운 클래스도 이를 구현해서는 안 된다. final 클래스라면 Cloneable을 구현해도 위험이 크지 않지만, 성능 최적화 관점에서 검토한 후 별다른 문제가 없을 때만 드물게 허용해야 한다.(아이템67) 기본 원칙은 복제 기능은 생성자와 팩터리를 이용하는 것이 ‘최고’라는 것이다. 단 배열만은 clone 메서드 방식이 가장 깔끔한, 이 규칙의 합당한 예외라 할 수 있다.

This post is licensed under CC BY 4.0 by the author.

[이펙티브자바] 아이템12-toString을 항상 재정의하라

[이펙티브자바] 아이템14-Comparable을 구현할지 고려하라