[아이템 13] clone 재정의는 주의해서 진행하라

clone 메서드를 사용하려면 Cloneable을 구현해야함
그렇지 않으면 CloneNotSupportedException 예외를 던짐

복제가 제대로 이루어지려면 다음의 규약을 만족해야 한다


x.clone() != x
-> 원본 객체와 복사된 객체는 서로 다르다
-> 논리적 동치

x.clone().getClass() == x.getClass()
-> 해당 클래스와 모든 상위 클래스가 super.clone으로 호출하는 관례를 따르면 참임, 반드시 만족해야 하는 것은 아님

x.clone().equals(x) 
-> 일반적으로 참, 필수 아님

clone 메서드 재정의할 때 주의사항

모든 필드가 기본 타입이거나 불변 객체를 참조하면 super.clone만 호출하면 됨

클라이언트가 형변환하지 않아도 되게끔 해주자
재정의한 메서드의 반환 타입은 상위 클래스의 메서드가 반환하는 타입의 하위 타입일 수 있다 (공변 반환 타이핑)

-> 가변 객체를 참조하지 않는 경우

클래스가 가변 객체를 참조할 때는?

-> 복제 후에도 원복 객체와 복제본이 모두 같은 배열을 참조하기 때문에 재앙으로 이어질 수 있음

해결 방법
재귀적 호출

-> 배열이 final이면 사용할 수 없음. 경우에 따라, final 한정자를 제거해야 할 수도 있음

재귀적 호출만으로 충분치 않은 경우


-> 복제본은 자신만의 버킷 배열을 갖지만, 배열 안에 있는 연결리스트는 여전히 원본과 같은 것을 참조하고 있음

해결 방법
1.


-> 리스트가 길면 스택 오버플로우가 발생할 위험이 있기 때문에 사용할 수 없음


  1. -> 재귀 호출 대신 반복문을 사용해 스택 오버플로우를 피함

  2. 원본 테이블의 put(key, value) 메서드를 호출해 같은 내용의 bucket을 만들어 복제하는 방법
    -> 속도가 느림

하위 클래스에서 재정의될 메서드를 호출하면 안된다

public class Sup implements Cloneable{
    String type;

    public Sup() {
        this.type = "super";
    }

    public void overrideMe() {
        System.out.println("super method");
    }

    @Override
    public Sup clone() {
        try {
            overrideMe();
            return (Sup) super.clone();
        } catch (CloneNotSupportedException e) {
            throw new RuntimeException();
        }
    }
}

public class Sub extends Sup {
    String temp;
    @Override
    public void overrideMe() {
        System.out.println("sub mehtod");
        System.out.println(temp);
        type = "sub";
    }

    @Override
    public Sub clone() {
        Sub clone = (Sub) super.clone();
        clone.temp = "temp";
        return clone;
    }

}

(출처 - https://javabom.tistory.com/15)

상속해서 쓰기 위한 클래스는 Cloneable을 구현하면 안된다

아직 cloneable을 구현한 클래스가 아니라면 복사 생성자와 복사 팩터리를 사용할 수 있다
-> Cloneable/clone 방식보다 장점이 훨씬 많다
-> 대표적으로 복제본의 타입을 직접 선택할 수 있다

좋은 웹페이지 즐겨찾기