[JAVA] 깊은복사 (clone , 복사 생성자)
1. 깊은 복사 vs 얕은복사
얕은 복사는 단순히 객체를 복사하는 것을 의미한다. 객체 자체를 복사했기때문에 모체인 p의 맴버변수가 수정되면 p2의 맴버 변수값 또한 바뀌게 된다. p2 는 그냥 힙에 있는 p의 값을 가리키고 있을 뿐이다.
Person p = new Person();
Person p2 = p;
p.setAge = 10;
println(p2.getAge()) // 10
객체가 아닌 원시타입의 변수는 다음 처럼 대입하더라도 그 메모리가 힙에 생성되는 것이 아니기 때문에 자연스럽게 깊은 복사가 이루어진다. 그러나 객체나 배열에 대해서 독립적으로 복사해서 사용하는 깊은 복사를 사용하려면 뒤에 나올 두 방법을 사용하여 구현할 수 있다.
2. clone 을 통한 깊은복사 구현
clone 메서드를 오버라이딩 하여 사용하면 깊은 복사를 사용할 수 있다. 이때 조심해야할 점이 있다.
만약 클래스 안에 또다른 객체나 배열이 존재한다면 이 값은 clone 메서드로 복사 되지 않는다.
이럴 때는 내부 객체의 클래스에서 clone 메서드를 오버라이딩 해준뒤 최종적으로 깊은 복사하고 싶은 객체의 clone메서드 구현체에서 들고 있는 객체를 복사하여 대입해주어야한다.
public class Phone {
...
@Override
protected Object clone() throws CloneNotSupportedException {
Phone phone = (Phone) super.clone();
phone.battery = (Battery) this.battery.clone();
return phone;
}
}
public class Battery implements Cloneable{
...
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
clone 메서드는 복사 변수에 대해 새로운 메모리를 할당해주고 그 안에 값들을 모체의 값들을 그대로 대입해주는 방식이다.
2-1 . Clonable 인터페이스
일반적으로 인터페이스에는 메서드가 존재하고 이를 강제하게 설계되어있지만 Clonable 인터페이스는 마크인터페이스로 복제가 가능하다는 표시만해준다. 따라서 Clonable 인터페이스를 implement 하지 않아도 clone 메서드를 오버라이딩 할 수 있다.
2-1 . Clone 함수의 단점
- 캐스팅 : Object 반환 형으로 사용할 clone 하여 사용할때마다 캐스팅이 필요하다.
- final 맴벼 제어 : 복사 객체가 final 맴버 변수를 가진다면 이를 제어할 수 없습니다.
3. 복사 생성자를 통한 깊은 복사
위와 같은 단점을 해결할 수 있는 복사 생성자 입니다.
위에는 객체를 들고 있을때의 복사
아래는 배열을 들고 있을 때의 복사 예시입니다.
Class Stage{
// 일반 생성자
public Stage(int stageNum , int height, int width) {
this.stageNum = stageNum;
this.height = height;
this.width = width;
}
// 복사 생성자
public Stage(final Stage stage) {
this(stage.stageNum, stage.height, stage.width);
}
}
Class Stage{
// 일반 생성자
public Stage(int stageNum, int[][] map, int height, int width) {
this.stageNum = stageNum;
this.map = map;
this.height = height;
this.width = width;
for (int i = 0; i < height; i++) {
for (int j = 0; j < width; j++) {
... // 맵 생성 과정
}
}
}
// 복사 생성자
public Stage(final Stage stage) {
this(stage.stageNum, stage.map, stage.height, stage.width);
// map은 배열 이기 때문에 다음 과 같은 복사 과정이 필요하다.
int[][] cloneMap = new int[stage.width][stage.height];
for (int i = 0; i < height; i++) {
for (int j = 0; j < width; j++) {
cloneMap[j][i] = stage.map[j][i];
}
}
this.map = cloneMap;
}
...
}