일급 컬렉션이란?


public class Cards {
    private final List<Card> cards;
 
    // ...
}

컬렉션을 포장하고 포장된 컬렉션 외에 다른 필드가 없는 상태를 일급 컬렉션이라고 한다.

왜 쓰는건데?


// Dealer.class
public class Dealer {
    private List<Card> cards;
    // ...
    public Dealer(List<Card> cards) {
        validate(cards)
        this.cards = cards;
    }
    
    private void validate(List<Card> cards) {
    	// ...
    }
    // ...
}
 
// Player.class
public class Player {
    private List<Card> cards;
    // ...
    public Player(List<Card> cards) {
        validate(cards)
        this.cards = cards;
    }
    
    private void validate(List<Card> cards) {
    	// ...
    }
    // ...
}

카드패 라는 같은 의미를 지닌 컬렉션을 DealerPlayer 두 개의 클래스가 사용할 경우, 검증이나 카드를 추가하는 등의 기능을 두 클래스에 모두 작성해야한다.

그래서?

이렇게 될 경우, 중복되는 코드가 생기며 클래스의 역할이 무거워지는 단점이 생긴다.

때문에, 일급 컬렉션을 통해 상태와 로직을 따로 관리하여 로직이 사용되는 클래스의 부담을 줄일 수 있고, 중복되는 코드 역시 줄일 수 있다.

일급 컬렉션은 불변이다?


결론부터 말하자면 일급 컬렉션은 불변성을 보장하지 않으며, 보장 될 필요가 없다.

public class Cards {
    private final List<Card> cards;
    // ...
    public List<Card> getCards() {
        return cards;
    }
}

위와 같이 setter 를 구현하지 않아도 Cards 내부에 있는 cards 에 변화를 줄 수 있다.

이는 방어적 복사를 내부에서 수행하지 않았기 때문이다.

자세한 내용은 불변에 관하여 를 참고해 보는 것이 좋겠다.

불변으로 만드려면?

이 또한 불변에 관하여 를 참고해 보면 정확한 해답을 알 수 있다.

방어적 복사를 통해 생성 시 사용된 컬렉션과의 주소값을 끊어주고 getter 를 통해 객체의 컬렉션을 가져올 때 방어적 복사를 통해 한 번 더 주소값을 끊어주는 것이 중요하다.

결론


  • 일급 컬렉션을 통해 상태와 로직을 따로 관리하여 해당 컬렉션을 사용하는 클래스의 부담을 줄일 수 있다.
  • 일급 컬렉션을 통해 해당 컬렉션을 사용하는 여러 클래스에서 발생할 수 있는 중복코드를 줄일 수 있다.
  • 일급 컬렉션은 불변성을 보장하지 않아도 되며 불변으로 만들고자 할 경우 방어적 복사를 활용한다.

References