[Java] 클래스 복제(Clonable, Reflection)


Development note/Java  2019. 7. 15. 23:22

안녕하세요. 명월입니다.


이 글은 Java에서 클래스를 복제(Clonable, Reflection)하는 방법에 대한 글입니다.


예전에 C#에서 클래스 복제에 대한 글을 작성한 적이 있습니다.

링크 - [C#] Reflection를 이용한 클래스 복제


사실 클래스 복제라는 것이 쉬울 것 같으면서 의외로 간단하지 않은 처리입니다.

클래스 복제는 디자인 패턴에서도 프로토 타입 패턴과 관계가 있습니다.

링크 - [Design pattern] 프로토타입 패턴 (Prototype pattern)


public class Example {
  public Example() {
    Node node = new Node();
    Node node1 = node.clone();
    // node과 node1의 해쉬 코드는 다르므로 서로 다른 클래스로 복제가 되었다.
    System.out.println(node.hashCode());
    System.out.println(node1.hashCode());
  }

  public static void main(String[] args) {
    new Example();
  }
}

// clone 함수를 사용하기 위해서는 Cloneable 인터페이스를 상속받는다.
class Node implements Cloneable {
  private String data;

  public void setData(String data) {
    this.data = data;
  }

  public void print() {
    System.out.println(data);
  }

  // clone을 재정의한다.
  @Override
  public Node clone() {
    try {
      // clonable 인터페이스를 상속받으면 복제함수를 사용할 수 있다.
      return (Node) super.clone();
    } catch (Throwable e) {
      return null;
    }
  }
}

위 예제는 Clonable를 사용해서 클래스를 복제하는 방법입니다. 이게 java에서 가장 표준적으로 복제하는 방법입니다.

그러나 Cloneable를 상속받지 않았을 경우, clone 함수를 만들지 않았을 경우에는 어떻게 복제를 할까요?


getter, setter가 있으면 getter, setter를 이용해서 복제하면 되지만 그렇지 않은 경우에는 private의 값을 복제할 수가 없습니다.

그럴때는 Reflection을 이용해서 복제할 수 있습니다.


링크 - [Java강좌 - 35] Reflection 기능 - Variable 편

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;

public class Example {
  // 클래스 복제
  public Object clone(Object obj) {
    try {
      // 복제할 클래스 타입을 취득한다.
      Class<?> clz = obj.getClass();
      // 복제할 클래스의 생성자를 취득한다.
      Constructor<?> cons = clz.getDeclaredConstructor();
      // 클래스를 생성한다.
      Object clone = cons.newInstance();
      // 클래스 내부의 변수를 취득한다.
      for (Field field : clz.getDeclaredFields()) {
        // private, protected 접근자도 접근할 수 있게 바꾼다.
        field.setAccessible(true);
        // 원본 클래스로부터 데이터를 취득한다.
        Object data = field.get(obj);
        // 복제할 클래스로 데이테를 입력한다.
        field.set(clone, data);
      }
      // 복제된 클래스를 리턴한다.
      return clone;
    } catch (Throwable e) {
      return null;
    }
  }

  public Example() {
    // 클래스 생성
    Node node = new Node();
    // Data를 넣는다.
    node.setData("Hello world");
    
    // 클래스 복제
    Node node1 = (Node) clone(node);

    // 해쉬 코드가 다르니 서로 다른 클래스이다.
    System.out.println(node.hashCode());
    System.out.println(node1.hashCode());
    
    // 복제된 클래스에도 데이터가 잘 들어가 있다.
    node1.print();
  }

  public static void main(String[] args) {
    new Example();
  }
}

class Node {
  private String data;

  public void setData(String data) {
    this.data = data;
  }

  public void print() {
    System.out.println(data);
  }
}

여기까지 두가지 패턴의 클래스 복제를 소개했습니다. 클래스 복제는 실제 프로그래밍할 때도 많이 사용되는 부분입니다. 기본적으로 clone함수를 사용해서 복제하는 것이 기본이긴 한데, 상황에 따라 clone를 사용할 수 없으면 Reflection을 통해서도 복제가 가능합니다.


여기까지 Java에서 클래스를 복제(Clonable, Reflection)하는 방법에 대한 설명이었습니다.


궁금한 점이나 잘못된 점이 있으면 댓글 부탁드립니다.