반응형
Notice
Recent Posts
Recent Comments
Link
«   2024/07   »
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30 31
Archives
Today
Total
관리 메뉴

코딩하기 좋은날

new Vs Clone Vs Copy constructor(feat. Clone vs Copy) 본문

Kotlin

new Vs Clone Vs Copy constructor(feat. Clone vs Copy)

huiung 2022. 7. 2. 18:26
반응형

Java의 new, Clone, Copy constructor 그리고 kotlin의 copy 연산자들을 한번 비교해보자.

 

참조: https://dzone.com/articles/java-cloning-copy-constructor-vs-cloning

 

Java Cloning: Copy Constructors vs. Cloning - DZone Java

Object.clone() has fallen out of favor with devs, so let's see why. Here we consider cloning and compare it to serialization and copy constructors.

dzone.com

new operator in Java

Java에서 새로운 object를 만들 때 사용하는 keyword. heap 영역에 메모리를 할당해주고 메모리 주소 반환 및 해당 Class의 생성자를 실행 시켜준다.

Object.clone()

Java의 Obejct 클래스에는 clone() 메서드가 정의되어 있다. clone 메서드는 native 메서드로 해당하는 객체의 모든 필드를 복사하여 새로운 객체에 넣어 반환하는 동작을 수행한다.

protected Object clone() throws CloneNotSupportedException {
        if (!(this instanceof Cloneable)) {
            throw new CloneNotSupportedException("Class " + getClass().getName() +
                                                 " doesn't implement Cloneable");
        }

        return internalClone();
    }

위 코드가 Object 내의 clone() 함수의 코드인데 Cloneable을 구현하지 않은 instance라면 CloneNotSupportedException을 던진다.

Cloneable interface

Cloneable interface는 아무 메서도드 선언되지 않은 Marker interface(Serializable 등) 다.

Cloneable 이 구현되지 않은 클래스의 객체에서 clone 함수를 호출하게 되면 CloneNotSupportedException 예외를 발생시키게 된다.

즉 Cloneable 을 implements 함으로써 Object의 Clone 함수 호출을 할 수 있음을 의미한다.

clone()의 동작

Object 의 clone 함수는 필드의 모든 내용을 복사할 때 shallow copy로 결과를 반환한다. 이는 사용 시 여러 불편한 점과 필수적인 추가 작업이 필요함을 뜻한다.

해당 클래스의 필드중, Primitive Type이 아닌 필드(array, ...)가 존재하는 경우 deep copy로 처리하기 위해서는 해당 필드 객체에 대한 메모리를 새로 할당하고 모든 Primitive 값을 새 메모리에 복사해야 한다. 따라서 아래와 같이 clone 함수를 재정의 해야한다.

public class Example implements Cloneable{

    String[] arr = {"1", "2", "3"}

    public Example() {

    }

    @Override
    public Object clone() throws CloneNotSupportedException {
			Example a = super.clone();
			a.arr = arr.clone();
    }

}

Clone의 단점

  • Cloneable interface를 implements 해야 한다.
  • deep copy를 위해서는 clone 함수를 재정의 해야 한다.
  • CloneNotSupportedException 을 throw 하기 때문에 모든 하위의 호출 스택에서 매번 예외처리를 해주어야한다.
  • super.clone() 호출 시 부모타입의 객체를 가져오므로 반드시 donw casting으로 데이터 형을 맞춰주어야한다.
  • final로 정의된 필드는 선언과 동시에 초기화 되거나 단 한번 수행되는 생성자나 초기화 블럭에 의해 초기화 되어야 한다. 하지만 clone()함수는 함수를 통한 객체 반환이므로 생성자 및 초기화 블럭을 사용할 수 없습니다. 따라서 final필드는 임의로 제어할 수 없다.

Copy Constructor

객체의 복사(deep copy)를 위해 Copy Constructor를 구현하는 방법이 있다.

class Tree {
	int height;
	Apple apple;
	public Tree(Tree t){
		this.height = t.height;
		this.apple = new Apple(t.apple);
	}
}

Copy Constructor의 장점

  • 인터페이스 구현 및 예외 처리 불필요
    • Cloneable 및 CloneNotSupportedException
  • 타입 downcasting 불필요
  • 객체 construtor 제어
    • clone() 메서드 호출 시 construtor를 동작시킬 수 없으나, 복사와 동시에 임의의 생성자를 호출가능
    • 따라서 final 멤버도 복사하거나 제어 가능

결론

많은 개발자들 사이에서, clone()은 몇 가지 실수로 설계되었으므로 피하는 것이 가장 좋다고 한다.

Effective Java에 의하면, array를 clone 할때를 제외하고는 사용하지 않는 것을 추천하고 있다.

array를 copy할때는, 몇가지 이유로 다른방법(System.arraycopy, 새 instance 생성..)보다 clone을 사용하는 것이 가장 빠른 방법이다. 그 이유는 clone은 TypeChecking을 하지 않으며, 일반적으로 array를 생성할때는 deafult value(0)으로 먼저 채워넣은 뒤 다른 값을 넣게 되는데 clone을 이용할 경우 instance 생성 직후 값을 할당하므로 이러한 과정이 없게 된다(Jit).

참조: https://stackoverflow.com/questions/46145826/why-clone-is-the-best-way-for-copying-arrays

Kotlin의 Copy Vs Java Clone

data class Example(
	val a: Int,
    	val b: ArrayList<Int>
)

decompile Example Data Class

@NotNull
public final Example copy(int a, @NotNull ArrayList b) {
Intrinsics.checkNotNullParameter(b, "b");
   return new Example(a, b);
}

단순히 클래스의 constructor에 값을 넣어주며 새로운 객체를 만들어주고 있다. 따라서 Java의 clone과 동일하게 shallow copy이며 deep copy가 필요한 경우 copy 함수를 새로 구현하던지 해당 필드에 새로운 객체를 만들어서 넣어주는 방법을 통해 deep copy가 가능하다.

protected Object clone() throws CloneNotSupportedException {
        if (!(this instanceof Cloneable)) {
            throw new CloneNotSupportedException("Class " + getClass().getName() +
                                                 " doesn't implement Cloneable");
        }

        return internalClone();
    }

/*
 * Native helper method for cloning.
 */
@FastNative
private native Object internalClone();

Clone은 Native함수로 내부적으로 필드를 복사해서 새객체를 만든다면, Copy는 new operator를 이용하여 생성자를 통한 새로운 객체 생성이라는 차이가 있는 것으로 보인다.

반응형

'Kotlin' 카테고리의 다른 글

Kotlin Coroutine - Suspension은 어떻게 동작하는가?  (0) 2022.07.31