자바에서 최상위 객체인 Object는 하위 객체들이 오버라이딩하여 사용하도록 설계된 메서드들이 있다. (equals, hashCode, toString, clone, finalize) 그리고 이 메서드들은 일반 규약이 존재하는데 이를 따르지 않으면 자바에서 제공하는 클래스와 함께 사용할 때 제대로 동작하지 않는다.
이번 장에서는 clone 메서드에 대해 설명한다.
Cloneable 인터페이스
clone 메서드는 Object 클래스에 정의되어 있고, 하위 객체가 오버라이딩하여 재정의하여 사용한다. 하지만 clone 메서드를 정상적으로 사용하기 위해서는 추가적으로 해당 클래스가 Cloneable 인터페이스를 implements 해야한다.
사실 이런 구현방식은 정말 기괴한 방식이다. Cloneable 인터페이스에 clone 메서드가 정의만 되어 있었어도 Cloneable 인터페이스를 구현하는 클래스는 강제적으로 clone 메서드를 구현해야 하기때문에 아주 상식적인 방식으로 clone의 동작 방식을 이해할 수 있었을 것이다.
하지만 자바는 Cloneable을 Marker Interface로 구현하였고, natvie 메서드인 Object.clone()에서 instanceof를 사용하여 Cloneable을 implement한 클래스인 경우 clone 메서드를 호출하고, Cloneable를 implement하지 않은 클래스인 경우 CloneNotSupportedException 예외를 던지도록 구현되어 있다.
아래는 Cloneable의 소스이다.
package java.lang;
/**
* 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
* <tt>Object.clone</tt> (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 <tt>clone</tt> 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 JDK1.0
*/
public interface Cloneable {
}
Object.clone() 메서드
위에서 언급한 것처럼 Object.clone의 동작방식은 기괴하지만 널리 사용되고 있으므로 알아둬야 한다. 먼저 Object.clone() 메서드는 아래와 같다.
/**
* Creates and returns a copy of this object. The precise meaning
* of "copy" may depend on the class of the object. The general
* intent is that, for any object {@code x}, the expression:
* <blockquote>
* <pre>
* x.clone() != x</pre></blockquote>
* will be true, and that the expression:
* <blockquote>
* <pre>
* x.clone().getClass() == x.getClass()</pre></blockquote>
* will be {@code true}, but these are not absolute requirements.
* While it is typically the case that:
* <blockquote>
* <pre>
* x.clone().equals(x)</pre></blockquote>
* will be {@code true}, this is not an absolute requirement.
* <p>
* By convention, the returned object should be obtained by calling
* {@code super.clone}. If a class and all of its superclasses (except
* {@code Object}) obey this convention, it will be the case that
* {@code x.clone().getClass() == x.getClass()}.
* <p>
* By convention, the object returned by this method should be independent
* of this object (which is being cloned). To achieve this independence,
* it may be necessary to modify one or more fields of the object returned
* by {@code super.clone} before returning it. Typically, this means
* copying any mutable objects that comprise the internal "deep structure"
* of the object being cloned and replacing the references to these
* objects with references to the copies. If a class contains only
* primitive fields or references to immutable objects, then it is usually
* the case that no fields in the object returned by {@code super.clone}
* need to be modified.
* <p>
* The method {@code clone} for class {@code Object} performs a
* specific cloning operation. First, if the class of this object does
* not implement the interface {@code Cloneable}, then a
* {@code CloneNotSupportedException} is thrown. Note that all arrays
* are considered to implement the interface {@code Cloneable} and that
* the return type of the {@code clone} method of an array type {@code T[]}
* is {@code T[]} where T is any reference or primitive type.
* Otherwise, this method creates a new instance of the class of this
* object and initializes all its fields with exactly the contents of
* the corresponding fields of this object, as if by assignment; the
* contents of the fields are not themselves cloned. Thus, this method
* performs a "shallow copy" of this object, not a "deep copy" operation.
* <p>
* The class {@code Object} does not itself implement the interface
* {@code Cloneable}, so calling the {@code clone} method on an object
* whose class is {@code Object} will result in throwing an
* exception at run time.
*
* @return a clone of this instance.
* @throws CloneNotSupportedException if the object's class does not
* support the {@code Cloneable} interface. Subclasses
* that override the {@code clone} method can also
* throw this exception to indicate that an instance cannot
* be cloned.
* @see java.lang.Cloneable
*/
protected native Object clone() throws CloneNotSupportedException;
clone 메서드는 native 메서드로 작성되어 있으며 Cloneable 인터페이스를 구현하지 않은 클래스가 해당 메서드를 사용할 경우 CloneNotSupportedException 예외를 던지도록 정의되어 있다.
clone 메서드도 마찬가지로 규약이 존재하지만 다른 메서드의 비해 규약이 느슨한다.
1. x.clone() != x will be true, and that the expression.
객체의 복사본을 만들어서 반환하는 것이기 때문에 원본과 복사본은 동일할 수는 없다.
2. x.clone().getClass() == x.getClass() will be true, but these are not absolute requirements.
원본과 복사본의 클래스 타입은 동일해야하지만, 반드시 그래야 하는 것은 아니다. 하지만 이 규약은 너무 느슨하다. clone을 사용하는 입장에서는 원본과 복사본의 타입이 동일 할 것이라고 생각한다
3. x.clone().equals(x) will be true, this is not an absolute requirement.
By convention, the returned object should be obtained by calling super.clone. If a class and all of its superclasses (except Object) obey this convention, it will be the case that x.clone().getClass() == x.getClass().
--- 추후 추가 작성
'Java > [책] 이펙티브 자바' 카테고리의 다른 글
[이펙티브 자바] 규칙13. 접근 제어(Access Control)는 최소화하자. (0) | 2022.02.14 |
---|---|
[이펙티브 자바] 규칙12. Comparable, Comparator 인터페이스 (0) | 2022.01.07 |
[이펙티브 자바] 규칙10. Object.toString() 귀찮아도 구현하자 (0) | 2021.12.13 |
[이펙티브 자바] 규칙9. Object.hashCode() 구현방법 (0) | 2021.11.28 |
[이펙티브 자바] 규칙8. Object.equals() 구현방법 (0) | 2021.11.23 |