1 minute read

방어적 복사란

생성자를 통해 초기화 할 때, 새로운 객체로 감싸서 복사해주는 방법이다. 외부와 내부에서 주소값을 공유하는 인스턴스의 관계를 끊어주기 위함이다. 방어적 복사를 하지 않는다면, 외부(원본) List에서 리스트에 값을 추가할 시, 내부의 List에도 값이 추가 될 것이다.

방어적 복사를 하지 않은 경우

public final class Cards {

    private final List<Card> cards;

    public Cards(List<Card> initialCards) {
        cards = initialCards;
    }
  
    public List<Card> getCards() {
        return cards;
    }
}


public class Application {

    public static void main(String[] args) {
        List<Card> initialCards = new ArrayList<>();
        initialCards.add(Card.valueOf(Symbol.DIAMOND, Denomination.FIVE));
        initialCards.add(Card.valueOf(Symbol.DIAMOND, Denomination.FOUR));

        Cards cards = new Cards(initialCards);
      	initialCards.add(Card.valueOf(Symbol.DIAMOND, Denomination.NINE)); // 원본 리스트에서 값 추가

        for (Card card : initialCards) { // initialCards는 당연히 3개가 나올테고
            System.out.print("card = " + card);
            System.out.println(", card content = " + card.getDenomination() + " " +card.getSymbol());
        }

        for (Card card : cards.getCards()) { // initalCards에 카드를 추가하기 전에, Cards 객체를 먼저 만들었으니까 사이즈가 2이지 않을까?
            System.out.print("card = " + card);
            System.out.println(", card content = " + card.getDenomination() + " " +card.getSymbol());
        }
    }

}

이 실행 결과는 어떻게 될까?
Cards 메서드로 조작하는 것이 아닌, Cards를 생성할 때, 인자로 들어올 initialCards리스트에서 새로운 Card 객체를 추가하였다.

인자로 들어올 리스트(외부)를 변경하니, Cards의 내부 List에서도 변경되었다. 이유가 뭘까?

image

인자로 들어올 리스트 initialCardsCards의 필드인 cards가 주소를 공유하고 있기 때문이다.
그러면, Cards 객체를 생성할 때, 원본과의 주소 공유를 끊어내보자.


방어적 복사를 한 경우

public final class Cards {

    private final List<Card> cards;

    public Cards(List<Card> initialCards) {
        cards = new ArrayList(initialCards); // new ArrayList<>()를 이용해 복사본을 만들었다.
    }
  
    public List<Card> getCards() {
        return cards;
    }
}

new ArrayList<>(initialCards)를 통해 새로운 주소값을 참조하도록 복사하였다.
이렇게 되면 외부에서 넘겨주는 List와 내부적으로 사용하는 List가 참조하는 값이 다르기 때문에 외부에서 제어가 불가능해진다.

image

방어적 복사를 함으로써, 원본 리스트와 객체 내부 리스트의 참조 값이 달라지게 되었다.

그러면, Cards의 리스트는 외부의 변경에 안전하게 되었을까? 아니다. getCards().add()를 한다면 새로운 값이 추가가 될 것이다. 이것 또한 막아주는 방법이 있다. 이 내용은 다음 포스팅의 copyOf()Collections.Unmodifiable을 비교하면서 다루겠다.

Categories:

Updated: