본문 바로가기

Language & Framework/Java

자바 Object 클래스의 기본 메서드(3) clone() 얇은 복사와 깊은 복사

 

JS에서도 처음 얇은 복사 깊은 복사가 나오면 이게 뭔 소리인가 싶은데, JAVA도 별 다를 거 없다.

일단 clone() 메서드의 기본적인 활용법부터 살펴보자.

 

class CloneClass implements Cloneable {
    int num;
    CloneClass(int num) {
        this.num = num;
    }

    @Override
    public CloneClass clone() {
        Object obj = null;
        try {
            obj = super.clone();
        } catch (CloneNotSupportedException e) {
            System.out.println("실패");
        }
        return (CloneClass)obj;
    }
}

 

clone 메서드는 CloneNotSupportedException 예외를 반드시 처리해줘야 한다.

빈 객체를 생성한 후 해당 객체에 복사할 객체를 대입하고 객체를 리턴하면 이 집 복사 맛집이다.

결과를 확인해보자.

 

public class Clone {
    public static void main(String[] args) {
        CloneClass clone1 = new CloneClass(10);
        CloneClass clone2 = clone1.clone();

        System.out.println(System.identityHashCode(clone1));
        System.out.println(System.identityHashCode(clone2));
        //1975012498
        //1808253015

        clone1.num = 5;

        System.out.println(clone1.num);
        System.out.println(clone2.num);
        //5
        //10
    }
}

 

identityHashCode는 모든 객체에 대해 항상 다른 해시코드값을 반환할 것을 보장한다. 그래서 clone1과 clone2가 서로 다른 객체라는 것을 알 수 있다.

clone1의 값을 바꿔도 clone2의 값은 변하지 않는다.

그러나 복사할 객체의 값이 다른 객체의 참조 값이면 어떻게 될까?

 

 

class Animal {
    int size;
    Animal (int size) {
        this.size=size;
    }
    public String toString() {
        return "size : " + this.size;
    }
}

class Lion implements Cloneable {
    Animal size;
    Lion(Animal size) {
        this.size = size;
    }

    public Lion swallowCopy() {
        Object obj = null;
        try { obj = super.clone(); }
        catch(CloneNotSupportedException e) {}
        return (Lion)obj;
    }
}

public class Clone2 {
    public static void main(String[] args) {
        Lion lion = new Lion(new Animal(100));
        Lion lion2 = lion.swallowCopy();
        
        lion.size.size = 20;
        lion2.size.size = 30;
        
        System.out.println(lion.size);
        System.out.println(lion2.size);
        // 30
        // 30
        }
}

 

아까랑 전혀 다를 것 없는 방식이지만 객체의 값이 참조변수가 되니 전혀 다른 결과가 나타나고 있다.

이것을 swallow copy(얕은 복사)라고 한다.

 

악필 죄송 ㅎ

lion의 참조 변수를 그대로 복사하니 lion2의 참조 변수도 lion과 같은 객체를 참조하고 있는 것이다.

복사한 객체의 참조 변수를 새로 만들려면 어떻게 해야할까?

복사하는 과정에 참조할 객체까지 새로 만들어주면 된다.

 

 

class Lion implements Cloneable {
    Animal size;
    Lion(Animal size) {
        this.size = size;
    }

    public Lion deepCopy() {
        Object obj = null;
        try { obj = super.clone(); }
        catch(CloneNotSupportedException e) {}
        Lion cloneObj = (Lion)obj;
        cloneObj.size = new Animal(this.size.size);
        return cloneObj;
    }
}

public class Clone2 {
    public static void main(String[] args) {
        Lion lion = new Lion(new Animal(100));
        Lion lion2 = lion.swallowCopy();
        Lion lion3 = lion.deepCopy();

        System.out.println(lion.size);
        System.out.println(lion2.size);
        System.out.println(lion3.size);

        lion.size.size = 20;
        lion2.size.size = 30;
        lion3.size.size = 40;

        System.out.println(lion.size);
        System.out.println(lion2.size);
        System.out.println(lion3.size);
        // 30
        // 30
        // 40
    }
}

obj에 복사한 객체의 주소를 대입하는 것까지는 기존과 같으나 이번에는 new Animal(this.size.size)로 클론할 때 기존 참조 변수가 참조하던 객체의 값을 복사한 객체까지 새로 생성했다. 사실 나도 용어가 잘 정리되지 않은 상태라 말이 복잡하다..ㅎ..

아무튼 목표했던 deep copy(깊은 복사) 구현을 성공했다.