Cloneable
•
Cloneable은 해당 클래스가 복제해도 되는 클래스임을 명시하는 용도의 믹스인 인터페이스(mixin interface)이다.
•
Cloneablem의 가장 큰 문제는 Object에 clone 메서드가 protected로 선언되어 있어, Cloneable을 구현하는 것만으로는 외부 객체에서 clone을 호출할 수 없다는 것이다.
•
Cloneable을 구현한 클래스는 clone 메서드를 public으로 제공하는데, 이를 통해 생성자 없이도 객체를 생성할 수 있게 된다.
•
clone 메서드의 규약은 다음과 같다.
◦
x.clone() != x
◦
x.clone().getClass() == x.getClass()
◦
x.clone().equals(x) == true
•
clone 메서드는 super.clone을 호출해 객체를 얻어 반환해야한다. 그렇지 않으면 하위 클래스에서 clone 메서드가 제대로 동작하지 않을 수 있다.
@Override
public PhoneNumber clone() {
try {
return (PhoneNumber) super.clone();
} catch (CloneNotSupportedException e) {
throw new AssertionError(); // 일어날 수 없다.
}
}
Java
복사
•
이와 같이 super.clone을 통해 호출하여, 자바의 공변 반환 타이핑(covariant return typing)을 이용해 사용하는 것이 권장된다.
•
다만 아래와 같이 클래스 내에 가변 인수를 가지면 조금 더 주의를 기울여야 한다.
public class Stack {
private Object[] elements;
private int size = 0;
public Stack() {
this.elements = new Object[16];
}
...
}
Java
복사
•
위의 클래스를 super.clone 호출 후 바로 반환하면, size 값은 제대로 가지지만 elements는 원본 Stack과 똑같은 배열을 참조하게 되어 불변식이 깨질 수 있다.
•
clone 메서드는 생성자와 같다고 말했는데, 그렇기 때문에 원본 객체에 아무런 영향을 미치지 않으면서 복제된 객체의 불변식을 보장해야한다.
@Override
public Stack clone() {
try {
Stack result = (Stack) super.clone();
result.elemnts = elements.clone();
return result;
} catch (CloneNotSupportedException e) {
throw new AssertionError();
}
}
Java
복사
•
그렇기 때문에 이와 같이 elements 배열의 clone을 호출하여 복제된 배열을 저장하도록 만드는 방법을 사용하자.
•
하지만 elements가 final이라면 위 방법은 사용하지 못하므로, 복제를 위해 일부 필드에서 final을 제거해야 할 수도 있다.
•
이는 Cloneable 아키텍처의 가변객체를 참조하는 필드는 final로 선언하라는 일반 용법과 충돌한다.
•
Cloneable은 이처럼 잘못 설계된 인터페이스로, 감안하고 사용해야한다.
•
하위 클래스가 생성되기도 전에 하위 클래스의 메서드를 호출하기 때문에 생성자는 하위 클래스에서 재정의 될 수 있는 메서드를 호출하면 사용해서는 안되는데, 이는 clone 메서드에서도 동일하다.
•
clone 메서드를 재정의하여 구현할 때는 throws 절을 없애야 한다. 그래야 사용하기 편하다.
•
상속용 클래스는 Cloneable을 구현해서는 안되는데, 이를 막는 방법은 두 가지가 있다.
•
첫 번째는 Object에서 구현한 방법처럼 제대로 동작하는 clone 메서드를 protected로 선언해두고 CloneNotSupportedException을 던질 수도 있다고 선언하는 방법이다. Object를 상속할 때처럼 하위 클래스에서 Cloneable 구현 방법을 선택할 수 있게 만들어주는 방법이다.
•
두 번째는 다음과 같이 clone을 동작하지 않게 구현하고 재정의도 못하게 막는 것이다.
@Override
protected final Object clone() throws CloneNotSupportedException {
throw new CloneNotSupportedException();
}
Java
복사
•
Cloneable을 구현한 클래스를 멀티 스레딩 환경에서 스레드 안전하게 사용하고 싶다면, clone 메서드 역시 적절하게 동기화 해주어야한다.
•
복사 생성자나 복사 팩터리 같은 더 나은 객체 복사 방식을 제공하는 것을 고려해보자.