아이템 33. 다중 안전 이종 컨테이너를 고려하라. - ksw6169/effective-java GitHub Wiki

타입 안전 이종 컨테이너 패턴(type safe heterogeneous container pattern)

  • 제네릭을 일반적으로 사용하면 하나의 컨테이너에서 사용할 수 있는 타입의 수가 제한된다. (Set<E>, Map<K, V> 등의 컬렉션과 ThreadLocal<T>, AtomicReference<T> 등의 단일 원소 컨테이너에 제네릭 사용. 여기서 Set<E>Set<String> 과 같이 사용하면 해당 Set은 String의 요소만 담을 수 있다.)
  • 하지만 때로는 유연한 처리를 위해 하나의 컨테이너에서 여러 타입의 데이터를 받아 각각의 데이터를 타입 안전하게 사용할 수 있는 컨테이너가 필요할 수도 있을 것이다. 이 경우에는 타입 안전 이종 컨테이너 패턴을 사용할 수 있다.
  • 타입 안전 이종 컨테이너 패턴이란 컨테이너 대신 키를 매개변수화한 다음 컨테이너에 값을 넣거나 뺄 때 매개변수화한 키를 함께 제공하여 제네릭 타입 시스템이 키와 값의 타입이 같음을 보장할 수 있게 하는 설계 방식을 말한다.
// 타입 안전 이종 컨테이너 패턴을 적용한 Favorites 클래스
public class Favorites {
    private Map<Class<?>, Object> favorites = new HashMap<>();

    // put을 수행할 데이터의 타입을 타입 토큰으로 받는다.
    public <T> void putFavorite(Class<T> type, T instance) {
        favorites.put(Objects.requireNonNull(type), type.cast(instance));
    }

    // get을 수행할 데이터의 타입을 타입 토큰으로 받는다.
    public <T> T getFavorite(Class<T> type) {
        return type.cast(favorites.get(type));
    }
}
public static void main(String[] args) {
    Favorites f = new Favorites();

    f.putFavorite(String.class, "Java");
    f.putFavorite(Integer.class, 0xcafebabe);
    f.putFavorite(Class.class, Favorites.class);

    String favoriteString = f.getFavorite(String.class);
    int favoriteInteger = f.getFavorite(Integer.class);
    Class<?> favoriteClass = f.getFavorite(Class.class);

    System.out.printf("%s %x %s%n", 
        favoriteString, favoriteInteger, favoriteClass.getName());
}

위 코드의 취약점

  • Class 객체를 로 타입으로 받으면 타입 안전성이 쉽게 깨질 수 있다. (이 문제는 instanceof 비교를 통해 해결 가능)
  • 실체화 불가 타입에는 사용할 수 없다. (ex. List.class 를 받지 못한다. List.class와 같이 받아야 한다. 이 방식에는 명확한 해결책은 없다.)

컨테이너(Container)

컨테이너란 다른 인스턴스를 담을 수 있는 클래스 또는 데이터 구조를 의미한다. 컬렉션 또한 객체들의 컨테이너라고 할 수 있으며, 책에서 나온 단일 원소 컨테이너란 단 한 개의 원소만을 담을 수 있다는 뜻에서 단일 원소 컨테이너라 말하는 듯 하다.


타입 토큰(type token)

타입 토큰이란 컴파일타임 타입 정보와 런타임 타입 정보를 알아내기 위해 메소드들이 주고 받는 class 리터럴을 말한다. 참고로 class 리터럴의 타입은 Class 가 아닌 Class<T> 다.


참고 자료

  • Effective Java 3/E
⚠️ **GitHub.com Fallback** ⚠️