[Effective java] 공통 메서드 정리 - clone
자바에서 제공하는 copy에는 두가지 종류가 있다.
shallow copy 와 deep copy가 있다. 아래 예제를 보자.
int[] a = {10, 20, 30, 40};
int[] b = a; //shallow copy
b = a.clone(); //deep copy
shallow copy를 하게 되면 객체의 주소값이 다른 객체에 할당이 될뿐 내부의 값자체가 카피되진 않는다.
내부의 값도 복사하기 위해선 deep copy를 해야하는데 자바에서는 clone이라는 메서드를 제공한다.
하지만 기본형 타입의 배열이 아닌 객체의 배열을 copy할때에는 큰 문제가 생길수 있다.
아래코드를 보자.
Member[] m1 = {new Member("홍길동","abcd@gmail.com")};
Member[] m2 = m1.clone();
m2[0].setEmail("abcd@naver.com");
이렇게 하면 과연 잘 복사가 될까?
복사가 안된다 m2 내부의 데이터를 변경하면 m1 도 같이 변경되는 아주 괴상한 현상을 볼수 있다. 그이유는 m1[i] 가 가리키는 객체가 m2[i] 가 가리키는 객체와 동일하기 때문이다.
일반적으로 객체가 복사된다는 뜻은 아래의 내용을 만족함을 의미한다.
x.clone() != x //참
x.clone().getClass() == x.getClass() //참
x.clone().equals(x) // 일반적으로 참 (간혹 거짓일수도 있음)
clone을 사용하려면 상속을 하고 있는 상황도 고려해야한다.
위의 그림처럼 상속 관계에 있다고 가정하자.
만약에 X 에서 만들어진 인스턴스인 x에서 x.clone() 메서드를 호출하면 X 의 객체가 응답할것이다.
그런데 Y에서 만들어진 인스턴스인 y에서 y.clone() 메서드를 별도의 오버라이딩 없이 호출하면 super class인 X의 clone이 호출되기 때문에 결과가 X의 객체가 나온다. 의도치 않은 결과가 나오는 것이다.
그렇기에 별도로 clone 메서드를 재정의해서 사용해야하지만, 보통 실무에서는 복사 생성자나 복사 팩터리를 만들어사용하는 대안이 있다.
복사 생성자 (conversion Constructor)
public X(X x) { ... };
복사 팩토리 (conversion Factory)
public static X newInstance(X x) { ... };
이렇게 되면 복사될 타입이 정확하게 명시되기 때문에 클래스를 확장해도 Clone 될때 문제가 생기지 않는다.