2009년 7월 27일 월요일

Java 객체 복사(Clone) Best Practice

effective java 2nd를 보면 자바 객체 복사(Clone)하는 방법에 대해서 자세하게 기술되어 있습니다.
객체를 복사하려면 복사하려는 객체에 Cloneable 인터페이스를 implement 하고 Object 객체의
clone 메서드를 override 해야 합니다. 하지만 단일 클래스일 경우는 괜찮지만
슈퍼 클래스-자식 클래스 와 같이 상속 구조의 클래스 형태는 고려해야 할 사항이
많습니다. 심지어는 복사의 기술적 매커니즘은 자바 기술 밖이라고 합니다.
왜그런지는 설명하기도 어렵고 이해하기도 어렵습니다. 즉 복사를 하려고 했을때
고려해야 할 사항이 너무 많습니다.

그래서 추천하는 복사 방법은 "copy construction" 이란 방법 입니다.
책에는 예제가 없어서 제가 외국 사이트에서 소스 얻어서 재구성 했습니다.

Super Class



import java.util.ArrayList;
import java.util.List;

public class Dog {
private final List names;
private int age;
private int weight;
private String str;
private String values[] = new String[2];

public Dog() {
names = new ArrayList();
}

protected Dog(Dog original) {
names = new ArrayList(original.names);
age = original.age;
weight = original.weight;
str = original.str;
System.arraycopy(original.values, 0, values, 0, original.values.length);
}

public int getAge() {
return age;
}

public void setAge(int age) {
this.age = age;
}

public int getWeight() {
return weight;
}

public void setWeight(int weight) {
this.weight = weight;
}

public String getStr() {
return str;
}

public void setStr(String str) {
this.str = str;
}

public String[] getValues() {
return values;
}

public void setValues(String[] values) {
this.values = values;
}

public List getNames() {
return names;
}

public Dog copy() {
return new Dog(this);
}

}


Child Class


public class Dalmatian extends Dog {

private int spots;

public Dalmatian() {
}

protected Dalmatian(Dalmatian original) {
super(original);
spots = original.spots;
}

public Dalmatian copy() {
return new Dalmatian(this);
}

public static void main(String[] ar) throws Exception {

Dalmatian dalmatian1;
Dalmatian dalmatian2;
String temp;

// 복사 source 클래스 셋팅
dalmatian1 = new Dalmatian();
dalmatian1.setAge(1);
dalmatian1.setStr("00000");
dalmatian1.getNames().add("xxxxx");
dalmatian1.getNames().add("yyyyy");
dalmatian1.getValues()[0] = "----------------";

// target 클래스 복사
dalmatian2 = dalmatian1.copy();

// 복사된 내용 확인 하기
System.out.println("= copy description =========");
System.out.println("copy age ==> " + dalmatian2.getAge());
System.out.println("copy str ==> " + dalmatian2.getStr());
System.out.println("copy names ");

for (int i = 0; i < dalmatian1.getNames().size(); i++) {
temp = (String) dalmatian1.getNames().get(i);
System.out.println(temp);
}
System.out.println("copy values == ");
System.out.println(dalmatian1.getValues()[0]);

System.out.println("= copy description =========");
System.out.println();

// 값은 메모리 주소를 사용하는지 값 과 hashcode 비교
System.out.println("= change value =========");
dalmatian2.setAge(2);
System.out.println("=age1 ==> " + dalmatian1.getAge());
System.out.println("=age2 ==> " + dalmatian2.getAge());
System.out.println();

dalmatian2.setStr("11111");
System.out.println("=str1 ==> " + dalmatian1.getStr());
System.out.println("=str2 ==> " + dalmatian2.getStr());
System.out.println("=str1 hc ==> " + dalmatian1.getStr().hashCode());
System.out.println("=str2 hc ==> " + dalmatian2.getStr().hashCode());
System.out.println();

dalmatian2.getNames().add("wwwww");
dalmatian2.getNames().add("zzzzz");
System.out.println("=names1 ");

for (int i = 0; i < dalmatian1.getNames().size(); i++) {
temp = (String) dalmatian1.getNames().get(i);
System.out.println(temp);
}
System.out.println("=names2 ");
for (int i = 0; i < dalmatian2.getNames().size(); i++) {
temp = (String) dalmatian2.getNames().get(i);
System.out.println(temp);
}
System.out.println("=names1 hc ==> " + dalmatian1.getStr().hashCode());
System.out.println("=names2 hc ==> " + dalmatian2.getStr().hashCode());
System.out.println();

dalmatian2.getValues()[0] = "====================";

System.out.println("=value1[0] ==> " + dalmatian1.getValues()[0]);
System.out.println("=value2[0] ==> " + dalmatian2.getValues()[0]);
System.out.println("=value1 hc ==> "
+ dalmatian1.getValues().hashCode());
System.out.println("=value2 hc ==> "
+ dalmatian2.getValues().hashCode());
System.out.println("= change value =========");

}
}


== Result OUT ==

= copy description =========
copy age ==> 1
copy str ==> 00000
copy names
xxxxx
yyyyy
copy values ==
----------------
= copy description =========

= change value =========
=age1 ==> 1
=age2 ==> 2

=str1 ==> 00000
=str2 ==> 11111
=str1 hc ==> 45806640
=str2 hc ==> 46760945

=names1
xxxxx
yyyyy
=names2
xxxxx
yyyyy
wwwww
zzzzz
=names1 hc ==> 45806640
=names2 hc ==> 46760945

=value1[0] ==> ----------------
=value2[0] ==> ====================
=value1 hc ==> 7699183
=value2 hc ==> 14285251
= change value =========

소스가 좀 기네요 public static void main 함수는 분석 하시지 마세요. ^^ 바로 Resut OUT을 보시면서 이해
하시면 될 것 같습니다.
여기서 중요한 건 슈퍼 클래스 생성자 함수 인데 배열은 값 복사가 메모리 주소 참조값을 그대로 복사하기
때 문에 반드시 "System.arraycopy(original.values, 0, values, 0, original.values.length);" 사용해서
deep copy를 해야 합니다.
객체 복사는 prototype 디자인 패턴에서도 사용하며, 실제 개발을 하다 보면 clone이 필요한 경우가
있습니다. 참고 하시기 바랍니다.

댓글 없음:

댓글 쓰기