Cloneable은 복제해도 되는 클래스임을 명시하는 용도의 믹스인 인터페이스임
믹스인? 여러 클래스가 공통적으로 사용할 수 있는 메서드를 포함하는 인터페이스를 통해, 다중 상속의 이점을 활용하려는 접근 방식
clone메서드가 선언된 곳이 Cloneable이 아닌 Object임
protected라서 Cloneable을 구현하는 것만으로는 외부 객체에서 clone 메서드를 호출할 수 없음
→ clone()
메서드를 사용하려면 해당 메서드를 포함하는 클래스에서 이 메서드를 오버라이딩 해야함!
class MyCloneableClass implements Cloneable {
// 클래스의 내용
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
public class Main {
public static void main(String[] args) {
MyCloneableClass original = new MyCloneableClass();
try {
// 외부에서 clone() 메서드 호출 가능
MyCloneableClass copy = (MyCloneableClass) original.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
}
}
super.clone()
위 내용을 고려해야 하기 때문에 clone 메서드를 오버라이딩하여 구현할 때, super.clone()이 필요함
clone()
메서드는 참조만 복사된다.
public class Stack{
private Object[] elements;
private int size = 0;
~~
}
위 클래스를 clone을 사용해 복제할 수 있도록 만든다면?
→ 반환된 Stack 인스턴스는 원본 Stack 인스턴스와 같은 배열을 참조하게 되므로 원본이나 복사본 중 하나를 수정하면 다른 하나에도 영향을 줌
위 문제를 해결하기 위한 방법 : elements 배열의 clone을 재귀적으로 호출!
@Override public Stack clone(){
try{
Stack result = (Stack) super.clone();
result.elements = elements.clone();
return result;
}
catch(CloneNotSupportedException e){
throw new AssertionError();
}
}
→ 배열의 clone()은 런타임 타입과 컴파일 타입 모두 원본 배열과 똑같은 배열을 반환
→ 배열의 clone()은 clone 기능을 제대로 사용하는 유일한 예
→ 하지만 위 방법은 elements가 final이었다면 동작하지 않음
public인 clone
메서드에서는 throws 절을 없애야 그 메서드를 사용하기 편하다.
상속용 클래스는 Cloneable을 구현해서는 안된다.
Cloneable
을 구현하면, 해당 클래스의 clone
메서드가 다른 서브클래스에서 잘못된 방식으로 동작할 수 있는 위험이 있기 때문clone()
을 구현해 protected
로 두고 CloneNotSupportedException
을 던질 수 있다고 선언하게 한다.clone
을 동작하지 않게 구현하고 하위 클래스에서 재정의하지 못하게 한다.Cloneable을 이미 구현한 클래스를 확장하려면 구현해야 함
clone 대신 복사 생성자와 복사 팩터리 메서드를 사용하는 것이 좋음
복사 생성자
자신과 같은 클래스의 인스턴스를 인수로 받는 생성자
public Yum(Yum yum){...};
복사 팩터리 메서드
public static Yum newInstance(Yum yum){...};
⇒ 위 두 방법은 Cloneable/clone 방식보다 안전함