Item 13. clone 오버라이딩은 주의해서 진행하라

clone

protected Object clone() throws CloneNotSupportedException {}
  • 해당 인스턴스의 복사본을 만들어 반환하는 메서드

    • 물리적 주소는 다르나, 동일한 클래스의 인스턴스이며 equals로 비교했을 때 같은 객체를 생성

  • clone 메서드는 protected이기 때문에 사실상 이 메서드는 외부에서 접근이 불가능

  • 때문에, Java에서는 Cloneable이라는 인터페이스를 제공해 이를 구현하는 클래스는 clone 메서드를 제공

    • Cloneable이 clone 메서드를 오버라이딩하도록 강제하는 인터페이스는 아님에 주의

Cloneable의 올바른 사용 예

class Item13 implements Cloneable {

    @Override
    public Item13 clone() {
        try {
            return (Item13) super.clone();
        } catch (CloneNotSupportedException e) {
            throw new AssertionError();
        }
    }

}
  • clone 메서드를 public으로 재정의하고, 해당 클래스를 반환하도록 함

  • 반드시 super.clone()을 호출해야 함

    • 모든 하위 클래스에서 이 규약을 지킨다면, 참조형을 제외한 필드들은 모두 잘 복사됨이 보장됨

    • 하지만 참조형 필드는 얕은 복사가 일어날 수 있음

  • 복잡한 가변 데이터 묶음을 갖는 클래스의 경우 clone 메서드의 오버라이딩이 단순히 super.clone()으로 끝나지 않으며, 이후에 이 가변 필드들을 재귀적으로 복사하는 일을 수행해야 함

    • 불변 데이터는 초기화 후 변경이 불가하므로 아예 복사가 어려움

      • 불변 참조형 필드(final reference field)가 포함된 클래스를 완전히 복사하려면 final 한정자를 제거해야 함

  • 위와 같은 사유로 Cloneable을 구현하여 clone 메서드를 오버라이딩하는 방식은 권장되지 않으며, 복사 생성자 또는 복사 팩터리(정적 메서드) 방식으로 대체할 것을 권고

복사 생성자, 복사 팩터리

복사 생성자

class Item13 {
    
    public Item13(Item13 item13) {
        // 여기서 필드들을 직접 세팅
    }
    
}
  • 복사 생성자는 자신과 같은 클래스의 인스턴스를 매개변수로 받아 필드들을 직접 세팅(복사)함

복사 팩터리

class Item13 {

    public static Item13 newInstance(Item13 item13) {
		    // 여기서 필드들을 직접 세팅
    }
    
}
  • 복사 생성자에 Item 1(생성자 대신 정적 팩토리 메서드) 패턴을 적용한 경우에 해당

복사 생성자, 복사 팩터리의 장점

  • 초기화 단계에 해당하므로 불변 필드들을 복사하는 것이 가능

  • Cloneable과 clone 메서드는 문서화된 규약에 의존하기 때문에 클라이언트가 이 규약을 지키지 않을 수 있다는 위험이 있는데, 위의 두 방식은 그러한 위험이 없음

    • Cloneable을 구현해도 clone 메서드를 오버라이딩하도록 강제하지 않음

    • super.clone()을 반드시 호출해야 함에도, 이를 강제하지 않음

  • 반드시 같은 클래스의 인스턴스를 받지 않아도 됨. 해당 클래스가 구현한 인터페이스를 매개변수로 전달받아도 됨

Last updated