다음의 코드 내용에서 출력을 예상해 보세요.
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]