반응형

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한 곳에 잘 작성해주도록 하자.

 

 

 

 

 

 

 

반응형