[09-2] clone( ), 얕은 복사(shallow copy), 깊은 복사 - Object클래스
clone( )
o clone( )메서드는 자신을 복제하여 새로운 인스턴스를 생성하는 역할을 한다.
o Cloneable인터페이스를 구현한 클래스의 인스턴스만clone( )을 통한 복제가 가능하다.
구현하지 않고 clone( )을 호출하면 예외가 발생한다.
o clone( )을 오버라이딩 하면서 접근제어자를 protected에서 public으로 변경해야 한다.
그래야 상속관계가 없는 다른 클래스에서 clone( )을 호출할 수 있다.
public class Test {
public static void main(String[] args) {
Point original = new Point(3, 5);
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();
얕은 복사와 깊은 복사
o 얕은 복사(shallow copy)란 원본을 참조하는 참조 값만 복사되는 것을 말한다.
o clone( )은 단순히 객체의 저장된 값을 복사할 뿐 참조하는 객체까지 복사되지는 않는다. 객체배열을 clone( )으로 복제하는 경우 하나의 원본으로 복제복이 같이 공유하게 되기에 완전한 복사라고 보기 어렵다.
o 깊은 복사(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( )코드는 객체를 생성 후 대입하는 코드를 추가하여 새로운 인스턴스를 참조하도록 했다.