본문 바로가기
☕️ JAVA/🦋 Effective Java

자바에서 객체 복사는 clone()이 아닌 복사 생성자, 팩토리를 사용하자

by kukim 2022. 2. 5.

이 글은 책 이펙티브 자바 3판 Item 13과 하단 Reference를 참고했습니다. 잘못된 내용이 있다면 편하게 말씀해주세요 🙏🏻

 

결론

객체를 복사할 때 특별한 경우(이미 Cloneable로 구현한 클래스로 사용하고 있을 때)를 제외하고 복사 생성자와 복사 팩토리를 사용하자

 


자바에서 객체를 복사할 때 어떻게 할까?

방법은 보통 2가지가 있다.

 

1. Object 객체의 clone() 메서드 + Cloneable 인터페이스 사용

2. 복사 생성자 또는 복사 팩토리 사용

 

1번의 경우 Object.clone() 메서드가 있다. 복사가 필요한 클래스에서 이를 오버 라이딩하여 사용한다. 이때 Cloneable 인터페이스를 받아 구현한다. Cloneable를 받지 않으면 clone()는 예외를 던지게 설계되어있기 때문이다. 하지만 Cloneable와 clone() 오버 라이딩하는 방법은 몇 문제가 있다. clone()의 엉성한 문서 규약, 번거로운 예외처리, 참조형 필드 객체는 단순 clone()이 아닌 딥카피 등 번거로운 게 많다.

위 문제로 이미 clone(), Cloneable로 구현되어있는 클래스를 제외하고는 복사 생성자나 팩터리를 사용하자

 

왜?

clone()의 엉성한 문서 규약을 지키지 않아도 된다. 불필요한 예외 처리하지 않아도 된다. 형 변환 필요 없다.

인터페이스 타입의 인스턴스를 인수를 받을 수 있다.

예를 들어 Collection이나 Map 타입에는 인터페이스 기반 복사 생성자, 팩토리 (collection에서는 변환 생성자, 변환 팩토리라고 함)를 제공한다. 이를 사용하면 원본 구현 타입에 얽매이지 않고 복사본 타입을 정할 수 있다. 

HashSet<Integer> set = new HashSet();
set.add(1);
set.add(2);

// 복사할 때 new TreeSet<>(set) 을 통해 HashSet인 set을 TreeSet으로 타입 변경 가능
TreeSet<Integer> copySet = new TreeSet<>(set);

복사 생성자

복사 생성자는 단순히 자신과 같은 클래스의 인스턴스를 인수로 받는 생성자를 말한다.

// 예 1
public Yum(Yum yum) { ... };

// 예 2
public class BoundedSet<T> {
    private final LinkedList<T> data;
    private final int capacity;

    // ...

    public BoundedSet(BoundedSet<T> other) {
        data = new LinkedList<>(other.data);
        capacity = other.capacity;
    }

    // ...
}

// 예 3
public class Stack {
    private Object[] elements;
    private int size = 0;
    private static final int DEFAULT_INITIAL_CAPACITY = 16;

    public Stack(Stack s) {
        this.elements = s.elements.clone();
        this.size = s.size;
    }
}

복사 팩터리

복사 팩토리는 복사 생성자를 모방한 정적 팩터리 메서드이다. (참고 : 객체 생성할 때 '생성자' 대신 '정적 팩터리 메서드'를 써볼까? 🏭)

// 예 1
public static Yum newInstance(Yum yum) { ... };

// 예 2
public class BoundedSet<T> {
    private final LinkedList<T> data;
    private final int capacity;

    // ...

    public static <T> BoundedSet<T> newInstance(BoundedSet<T> other) {
        return new BoundedSet<T>(other.data, other.capacity);

    }
    // ...
}

// 예 3
public class Stack {
    private Object[] elements;
    private int size = 0;
    private static final int DEFAULT_INITIAL_CAPACITY = 16;

    public static Stack newInstance(Stack s) {
        return new Stack(s.elements, s.size);
    }
}

 

⛓ Reference

이펙티브 자바 스터디 저장소 item 13 김민걸

소프트웨어의 품격 ch5 p205

댓글