Java 프로그래밍을 하다보면 복사를 분명 한 줄 알았는데 복사가 되지 않는 경우가 많다.
예를들어 B 객체에 이미 있는 A객체를 데이터를 그대로 복사하고 싶어 B = A를 한다는 경우나
나름 B = new A()를 했지만 복사가 되지 않고 참조가 되지 않는 경우들이 있다.
이를 코드를 통해 파악해보자.
이미 제공되는 Object의 Swallow Copy.
import java.util.ArrayList;
public class Main {
public static void main(String []args){
ArrayList<Integer> arrayList = new ArrayList<>();
arrayList.add(1);
arrayList.add(2);
arrayList.add(3);
arrayList.add(4);
ArrayList<Integer> cloneList = arrayList;
cloneList.add(5);
cloneList.set(1,10);
System.out.printf("cloneList :: ");
for(int i = 0 ; i < cloneList.size(); i++) {
System.out.print(cloneList.get(i) + " ");
}
System.out.printf("\narrayList :: ");
for(int i = 0 ; i < arrayList.size(); i++) {
System.out.print(arrayList.get(i) + " ");
}
System.out.printf("\nIs clone object ? " + (arrayList != cloneList));
}
}
cloneList :: 1 10 3 4 5
arrayList :: 1 10 3 4 5
Is clone object ? false
위의 코드에서 볼 수 있듯이 Object A = B로 복사는 할 수 있지만 얕은 복사가 일어나
A와 B는 같은 객체임을 알 수 있다.
따라서 이는 우리가 원하는 Deep copy가 아니다.
이미 제공되는 Object의 Deep Copy.
import java.util.ArrayList;
public class Main {
public static void main(String []args){
ArrayList<Integer> arrayList = new ArrayList<>();
arrayList.add(1);
arrayList.add(2);
arrayList.add(3);
arrayList.add(4);
ArrayList<Integer> cloneList = (ArrayList<Integer>) arrayList.clone();
cloneList.add(5);
cloneList.set(1,10);
System.out.printf("cloneList :: ");
for(int i = 0 ; i < cloneList.size(); i++) {
System.out.print(cloneList.get(i) + " ");
}
System.out.printf("\narrayList :: ");
for(int i = 0 ; i < arrayList.size(); i++) {
System.out.print(arrayList.get(i) + " ");
}
System.out.printf("\nIs clone object ? " + (arrayList != cloneList));
}
}
cloneList :: 1 10 3 4 5
arrayList :: 1 2 3 4
Is clone object ? true
Object A = B.clone()를 해주면 완벽히 복사를 해준다.
이는 우리가 원하는 Deep copy이다.
하지만 우리가 좀 더 자세히 알아야 하는 것은 Custom Object일 경우는 어떻게 해야하냐에 관해서이다.
Custom object swallow copy
import java.util.ArrayList;
public class Main {
public static void main(String[] args){
Company company = new Company(2019, "Crocus", new Part("blog"));
Company companyClone = company;
companyClone.year = 1010;
companyClone.part.name = "hello";
System.out.println("== companyClone == ");
System.out.printf("year :: " + companyClone.year + " name :: " + companyClone.name + " part.name :: " + companyClone.part.name);
System.out.println("\n== company == ");
System.out.printf("year :: " + company.year + " name :: " + company.name + " part.name :: " + company.part.name);
System.out.printf("\nis clone object ? " + (company != companyClone));
}
static class Company{
int year;
String name;
Part part;
public Company(int year, String name, Part part) {
this.year = year;
this.name = name;
this.part = part;
}
}
static class Part{
String name;
public Part(String name) {
this.name = name;
}
}
}
== companyClone ==
year :: 1010 name :: Crocus part.name :: hello
== company ==
year :: 1010 name :: Crocus part.name :: hello
is clone object ? false
이는 복사가 안됐음을 결과만 봐도 알 수 있고, 결국 참조하고 있는 형태라는것을 파악 할 수 있다.
Custom object coexist swallow copy and deep copy
public class Main {
public static void main(String[] args){
Part part = new Part("blog");
Company company = new Company(2019, "Crocus", part);
Company companyClone = null;
try {
companyClone = company.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
companyClone.year = 1010;
companyClone.name = "hello";
companyClone.part.name = "world";
System.out.println("== companyClone == ");
System.out.printf("year :: " + companyClone.year + " name :: " + companyClone.name + " part.name :: " + companyClone.part.name);
System.out.println("\n== company == ");
System.out.printf("year :: " + company.year + " name :: " + company.name + " part.name :: " + company.part.name);
System.out.printf("\nis clone object ? " + (company != companyClone));
System.out.printf("\nis part clone object ? " + (company.part != companyClone.part));
}
static class Company implements Cloneable{
int year;
String name;
Part part;
public Company(int year, String name, Part part) {
this.year = year;
this.name = name;
this.part = part;
}
@Override
protected Company clone() throws CloneNotSupportedException {
return (Company) super.clone();
}
}
static class Part{
String name;
public Part(String name) {
this.name = name;
}
}
}
== companyClone ==
year :: 1010 name :: hello part.name :: world
== company ==
year :: 2019 name :: Crocus part.name :: world
is clone object ? true
is part clone object ? false
Company 클래스에 Cloneable 인터페이스를 구현시켜주면 clone 사용이 가능해진다.
하지만 여기서 object가 clone됐는지 물을 때 true라 했지만 자세히 보면 part에 대한 객체는 복사가 되지 않았음을 알 수 있다.
이는 즉슨 객체를 복사해도 객체 내에 객체가 또 있다면 그것도 완벽히 복사를 해주어야 한다는 것이다.
Custom object deep copy
public class Main {
public static void main(String[] args){
Part part = new Part("blog");
Company company = new Company(2019, "Crocus", part);
Company companyClone = null;
try {
companyClone = company.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
companyClone.year = 1010;
companyClone.name = "hello";
companyClone.part.name = "world";
System.out.println("== companyClone == ");
System.out.printf("year :: " + companyClone.year + " name :: " + companyClone.name + " part.name :: " + companyClone.part.name);
System.out.println("\n== company == ");
System.out.printf("year :: " + company.year + " name :: " + company.name + " part.name :: " + company.part.name);
System.out.printf("\nis clone object ? " + (company != companyClone));
System.out.printf("\nis part clone object ? " + (company.part != companyClone.part));
}
static class Company implements Cloneable{
int year;
String name;
Part part;
public Company(int year, String name, Part part) {
this.year = year;
this.name = name;
this.part = part;
}
@Override
protected Company clone() throws CloneNotSupportedException {
Company company = (Company) super.clone();
company.part = (Part) company.part.clone();
return company;
}
}
static class Part implements Cloneable{
String name;
public Part(String name) {
this.name = name;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
}
== companyClone ==
year :: 1010 name :: hello part.name :: world
== company ==
year :: 2019 name :: Crocus part.name :: blog
is clone object ? true
is part clone object ? true
이제 완벽한 deep copy를 성공 할 수 있게 되었다.
Company 클래스에서 part에 대한 clone을 구현해주어야 한다.
custom object라면 위와 같이 clone를 이용 할 때 내부에 또다른 객체가 존재한다면 해당 객체를 clone해주는 과정을 override한 곳에 잘 작성해주도록 하자.
'Basic > Java' 카테고리의 다른 글
Java multi catch 구문 개념 및 이용 (0) | 2019.12.24 |
---|---|
자바의 try with resources 구문 (0) | 2019.12.16 |
Eclipse 자주 사용하는 단축키 모음 (0) | 2019.11.07 |
ReentrantLock이란? (0) | 2019.08.01 |
CountDownLatch를 이용한 java 병렬 프로그래밍 (0) | 2019.07.30 |