728x90
1. Cloneable 인터페이스
/**
* A class implements the <code>Cloneable</code> interface to
* indicate to the {@link java.lang.Object#clone()} method that it
* is legal for that method to make a
* field-for-field copy of instances of that class.
* <p>
* Invoking Object's clone method on an instance that does not implement the
* <code>Cloneable</code> interface results in the exception
* <code>CloneNotSupportedException</code> being thrown.
* <p>
* By convention, classes that implement this interface should override
* {@code Object.clone} (which is protected) with a public method.
* See {@link java.lang.Object#clone()} for details on overriding this
* method.
* <p>
* Note that this interface does <i>not</i> contain the {@code clone} method.
* Therefore, it is not possible to clone an object merely by virtue of the
* fact that it implements this interface. Even if the clone method is invoked
* reflectively, there is no guarantee that it will succeed.
*
* @author unascribed
* @see java.lang.CloneNotSupportedException
* @see java.lang.Object#clone()
* @since 1.0
*/
public interface Cloneable {
}
Cloneable 인터페이스는 java.lang 패키지에 포함되어있는 클래스로 복제해도 되는 클래스임을 명시하는 Mixin 인터페이스이다. (기능 추가 용도의 인터페이스 ex) Comparable, Serializable ) 위 처럼 인터페이스 내용은 비어있지만, Marker Interface로써 JVM에 마킹한 내용을 전달하여 특정 동작을 수행하도록 한다. Cloneable 같은 경우 clone 메소드의 동작 방식을 결정합니다. Cloneable 인터페이스를 구현한 클래스에서 clone을 호출한 경우 객체의 필드를 하나하나 복사한 객체를 반환합니다. (구현하지 않은 경우 CloneNotSupportedException를 던진다.)
2. Object의 clone 메소드
- x.clone() != x
- x.clone().getClass() == x.getClass() 일반적으로 참 하지만 반드시 X
- x.clone().equals(x) 참
- Cloneable가 구현 안될 경우 CloneNotSupportedException 발생
3. clone 재정의시 주의사항
- super.clone()의 반환값의 형변환을 명시적으로 해준다.
- super.clone 은 가변 참조 필드에 대해서 얕은 복사(Shallow Copy)를 진행하므로 참조 필드마다 clone 를 호출해준다.
- clone은 원본객체에 아무런 영향을 주면 안된다.
- 재정의 될수 있는 메소드를 호출하지 않는다.
- clone의 스레드 안전성 보장 (기본 Object의 clone은 스레드 안전하지 않음)
public class Stack implements Cloneable{
private Object[] elements;
private int size = 0;
private static final int DEFAULT_INITIAL_CAPACITY = 16;
public Stack() {
this.elements = new Object[DEFAULT_INITIAL_CAPACITY];
}
public void push(Object o) {}
}
...
@Override
public Stack clone() {
try {
Stack result = (Stack) super.clone();
result.elements = elements.clone(); //elements가 final이면 에러가 난다.
return result;
} catch(CloneNotSupportedException e) {
}
}
}
public class HashTable implements Cloneable {
private Entry[] buckets = ...;
private static class Entry {
final Object key;
Object value;
Entry next;
Entry(Object key, Object value, Entry next) {
this.key = key;
this.value = value;
this.next = next;
}
}
@Override
public HashTable clone() {
try {
HashTable result = (HashTable) super.clone();
result.buckets = buckets.clone(); //버킷의 연결리스트가 같은 값을 참조한다.
return result;
} catch(CloneNotSupportedException e) {
throw new Assertion();
}
}
}
4. 복사 생성자 와 복사 팩터리
- 잘못 설계된 Cloneable을 더 이상 사용할 필요가 없음(생성자 X, 엉성한 문서 규약와 인터페이스 설계)
- final 객체 필드에 대해서도 복사 가능해짐
- 형변환 필요 X
- 불필요한 검사 예외 X
- 인터페이스 타입의 변수로 복사 가능 (ex HashSet을 TreeSet 타입으로 복제 가능)
public Yum(Yum yum) {} //복사 생성자
public static Yum newInstance(Yum yum) {} // 복사 팩토리
'JAVA > Effective Java' 카테고리의 다른 글
item 25) 톱레벨 클래스는 한 파일에 하나만 담으라 (0) | 2021.01.15 |
---|---|
item 18) 상속보다는 컴포지션을 사용하라 (0) | 2021.01.11 |
item 12) toString을 항상 재정의하라 (0) | 2021.01.03 |
item 11) equals를 재정의하려거든 hashCode도 재정의하라 (0) | 2020.12.27 |
item 10) equals는 일반 규약을 지켜 재정의하라 (0) | 2020.12.27 |