Programming Language/JAVA

[JAVA] 깊은복사 & 얕은복사(clone(), Cloneable)

머직타드 2022. 6. 11. 23:54

다음의 코드 내용에서 출력을 예상해 보세요.

class Point{
	public int x, y;
	
	public Point(int x, int y) {
		this.x = x;
		this.y = y;
	}
	public void changePoint(int x, int y) {
		this.x = y;
		this.y = x;
	}
}
class Rectangle implements Cloneable{
	private Point upperLeft, lowerRight;
	
	public Rectangle(int x1, int y1, int x2, int y2) {
		upperLeft = new Point(x1, y1);
		lowerRight = new Point(x2, y2);
	}
	public void showRectangle() {
		System.out.printf("[%d, %d, %d, %d]\n", upperLeft.x, upperLeft.y, lowerRight.x, lowerRight.y);
	}
	public void changeRectangle(int x1, int y1, int x2, int y2) {
		upperLeft.changePoint(x1, y1);
		lowerRight.changePoint(x2, y2);
	}
	public Object Clone() throws CloneNotSupportedException{
		return super.clone();
	}
}
public class copyClass {

	public static void main(String[] args) {
		
		try {
			Rectangle originalRect = new Rectangle(1, 1, 9, 9);
			Rectangle copyRect = (Rectangle)originalRect.Clone();
			
			// originalRect만 값 변경
			originalRect.changeRectangle(2, 2, 5, 5);
			// 각 사각형 출력하기
			originalRect.showRectangle();
			copyRect.showRectangle();
		} catch (CloneNotSupportedException e) {
			e.printStackTrace();
		}
	}
}

처음에 originalRect: [1, 1, 9, 9]가 담긴 인스턴스를 생성하고, clone()메소드를 이용해 copyRect를 생성했습니다. 이 때, originalRect의 멤버 변수만 [2, 2, 5, 5]로 바꿔준 내용입니다.

그럼 copyRect는 clone()메소드로 복사를 해줬는데... 과연 어떤 값을 갖고 있는지 실행해 보겠습니다.

Console 출력 결과
[2, 2, 5, 5]
[2, 2, 5, 5]

 

분명 깊은복사를 수행하는 clone()메소드를 이용해 copyRect를 생성해줬음에도 불구하고, originalRect에 변경된 멤버변수값과 동일하게 copyRect에서도 변경되었음을 알 수 있습니다. 이유는 뭘까요?

 

Object 클래스 clone() 메소드는 인스턴스 변수에 저장되어 있는 값만 복사할 뿐이다.

 

 

결국 clone()메소드는 인스턴스 변수가 참조하는 대상까지 복사하지는 않기 때문에 이런 결과가 나왔습니다.

 

 

즉 Rectangle 클래스의 인스턴스 변수 upperLeft와 lowerRight의 참조 값이 복사되었을 뿐, 참조 변수가 가리키는 인스턴스(x, y)까지 복사가 된 것은 아니긴 때문입니다. 어찌 보면 Rectangle인스턴스만 깊은 복사가 진행되었고, Point 인스턴스는 얕은복사가 이루어진 셈이죠.

 

만일 Point인스턴스까지 깊은 복사를 해주고 싶으면 Clone()메소드를 오버라이딩하여 재 정의해주면 됩니다.

// Cloneable 상속
class Point implements Cloneable{
	public int x, y;
	
	public Point(int x, int y) {
		this.x = x;
		this.y = y;
	}
	public void changePoint(int x, int y) {
		this.x = y;
		this.y = x;
	}
	// Point 클래스에서의 clone() 구현
	public Object clone() throws CloneNotSupportedException{
		return super.clone();
	}
}
class Rectangle implements Cloneable{
	private Point upperLeft, lowerRight;
	
	public Rectangle(int x1, int y1, int x2, int y2) {
		upperLeft = new Point(x1, y1);
		lowerRight = new Point(x2, y2);
	}
	public void showRectangle() {
		System.out.printf("[%d, %d, %d, %d]\n", upperLeft.x, upperLeft.y, lowerRight.x, lowerRight.y);
	}
	public void changeRectangle(int x1, int y1, int x2, int y2) {
		upperLeft.changePoint(x1, y1);
		lowerRight.changePoint(x2, y2);
	}
	public Object Clone() throws CloneNotSupportedException{
		// Point 인스턴스까지 깊은복사 정의!
		Rectangle copy = (Rectangle)super.clone();
		copy.upperLeft = (Point)upperLeft.clone();
		copy.lowerRight = (Point)lowerRight.clone();
		
		return copy;
	}
}
public class copyClass {

	public static void main(String[] args) {
		
		try {
			Rectangle originalRect = new Rectangle(1, 1, 9, 9);
			Rectangle copyRect = (Rectangle)originalRect.Clone();
			
			// originalRect만 값 변경
			originalRect.changeRectangle(2, 2, 5, 5);
			// 각 사각형 출력하기
			originalRect.showRectangle();
			copyRect.showRectangle();
		} catch (CloneNotSupportedException e) {
			e.printStackTrace();
		}
	}
}
Console 출력 결과
[2, 2, 5, 5]
[1, 1, 9, 9]