언어/자바의 정석

[09-2] clone( ), 얕은 복사(shallow copy), 깊은 복사 - Object클래스

chan10 2021. 2. 25. 11:47

clone( )

o  clone( )메서드는 자신을 복제하여 새로운 인스턴스를 생성하는 역할을 한다.

Cloneable인터페이스를 구현한 클래스의 인스턴스만clone( )을 통한 복제가 가능하다.

    구현하지 않고 clone( )을 호출하면 예외가 발생한다.

 o  clone( )을 오버라이딩 하면서 접근제어자를 protected에서 public으로 변경해야 한다.

    그래야 상속관계가 없는 다른 클래스에서 clone( )을 호출할 수 있다.

 

public class Test {    
    public static void main(String[] args) {    
        Point original = new Point(35);
        Point copy = (Point)original.clone(); //복제하여 새로운 객체 생성
        System.out.println(original);
        System.out.println(copy);
    }
}
//clone 오버라이딩 시 Cloneable인터페이스를 구현해야 clone()을 호출할 수 있다.
class Point implements Cloneable {
    int x, y;
    
    Point(int x, int y){
        this.x = x;
        this.y = y;
    }
    public String toString() {
        return "x="+x+", y="+y;
    }
    public Object clone() { //접근제어자 public
        Object obj=null;
        try {
            obj =super.clone(); //반드시 예외처리 해주어야한다.
        } catch (CloneNotSupportedException e) { }
        return obj;
    }
}

 공변 반환타입

o  공변 변환타입(covariant return type)은 오버라이딩 시 조상 메서드의 반환타입을 자손 클래스의 타입으로 변경을 허용하는 것이다.

o  변 반환타입을 사용하면 조상 타입이 아닌 실제 반환되는 자손 객체를 반환타입으로 반환할 수 있다.

public Point clone() { //반환타입을 Object에서 Point로 변경
    Object obj=null;
    try {
        obj =super.clone();
    } catch (CloneNotSupportedException e) { }
    return (Point)obj; //Point타입으로 형변환한다.
}

        공변 반환타입 적용 전                              공변 반환타입 적용 후

Point copy = (Point)original.clone();            Point copy = original.clone();

 

얕은 복사와 깊은 복사

얕은 복사(shallow copy)란 원본을 참조하는 참조 값만 복사되는 것을 말한다.

o  clone( )은 단순히 객체의 저장된 값을 복사할 뿐 참조하는 객체까지 복사되지는 않는다. 객체배열을 clone( )으로 복제하는 경우 하나의 원본으로 복제복이 같이 공유하게 되기에 완전한 복사라고 보기 어렵다.

깊은 복사(deep copy)란 원본이 참조하고 있는 객체까지 복사하는 것을 말한다. 깊은 복사에서는 원본과 복사본이 서로 다른 객체이기에 원본이 변경되어도 복사본에 영향이 없다.

 

public class Test {    
    public static void main(String[] args) {    
        Circle c1 = new Circle(new Point(1,1), 2.0);
        Circle c2 = c1.shallowCopy();//얕은 복사
        Circle c3 = c1.deepCopy(); //깊은 복사
        
        System.out.println("c1 변경 전");
        System.out.println("c1= "+c1);
        System.out.println("c2= "+c2);
        System.out.println("c3= "+c3);
        c1.p.x=20;
        c1.p.y=20;
        c1.r = 10.0;
        System.out.println("c1 변경 후");
        System.out.println("c1= "+c1);
        System.out.println("c2= "+c2);//p값 변경
        System.out.println("c3= "+c3);//p값 유지
    }
}
 
class Circle implements Cloneable {
    Point p;
    double r;
    
    Circle (Point p, double r){
        this.p = p;
        this.r = r;
    }
    public Circle shallowCopy() {//얕은 복사
        Object obj = null;
        
        try {
            obj=super.clone();//Object clone()호출
        } catch(CloneNotSupportedException e) {    }
        return (Circle)obj;
    }
    public Circle deepCopy() {// 깊은 복사
        Object obj = null;
        try {
            obj = super.clone();            
        } catch (CloneNotSupportedException e) { }
        
        Circle c = (Circle)obj;
        c.p=new Point(this.p.x, this.p.y); //새로운 인스턴스 생성 후 대입
        return c;
    }
    @Override
    public String toString() {//toString 오버라이딩
        return "[p="+p+", r= "+r+"]";
    }
}
 
class Point {
    int x;
    int y;
    
    Point(int x, int y){
        this.x=x;
        this.y=y;
    }
    @Override
    public String toString() {//toString 오버라이딩
        return "(x="+x+", y= "+y+")";
    }
}

-  인스턴스 c1을 생성 후 얕은 복사로 c2를 생성하고 깊은 복사로 c3를 생성했다.

-  복사한 인스턴스 c1에서 Point 타입의 참조변수 p의 값을 변경하면 얕은 복사를 한 c2는 같은 객체를 참조하고 있으므로 같이 영향이 있지만, 깊은 복사를 한 c3는 새로운 Point 인스턴스를 생성하여 복사했기에 영향이 없다.

-  shallowCopy( )의 코드는 단순히 원본 객체가 가지고 있는 값만 복사하고 deepCopy( )코드는 객체를 생성 후 대입하는 코드를 추가하여 새로운 인스턴스를 참조하도록 했다.