아이템 33. 다중 안전 이종 컨테이너를 고려하라. - ksw6169/effective-java GitHub Wiki
- 제네릭을 일반적으로 사용하면 하나의 컨테이너에서 사용할 수 있는 타입의 수가 제한된다. (
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