[Effective Java] 5 - item 33 제네릭과 가변인수를 함께 쓸 때는 신중하라.
item 33 타입 안전 이종 컨테이너를 고려하라.
제네릭은 Set<E>, Map<K,V>
과 같은 컬렉션과 ThreadLocal<T>, AtomicReference <T>
같은 단일 원소 컨테이너에 흔히 사용된다. 이 모든 쓰임에서 매개변수화 되는 대상은 원소가 아닌 컨테이너 자신이며, 하나의 컨테이너에서 매개변수화 할 수 있는 타입 개수가 제한된다.
- Set에는 원소의 타입을 뜻하는 단 하나의 타입 매개변수만 있으면 된다.
- Map에는 키와 값의 타입을 위한 2개만 필요하다.
하지만 때로는 더 유연한 수단이 필요할 수 있는데, 이를 위해 도입된 것이 타입 안전 이종 컨테이너 (type safe heterogeneous container pattern) 이다.
타입 안전 이종 컨테이너 (type safe heterogeneous container pattern)
- 데이터베이스의 행은 임의 개의 열을 가질 수 있는데, 모든 열을 타입 안전하게 이용할 수 있다면 좋을 것이다. 이를 위해 컨테이너 대신 키를 매개변수화 한 다음, 컨테이너에 값을 넣거나 뺄 때 매개변수화 한 키를 함께 제공하자.
- 이와 같은 방식을 사용하면, 제네릭 타입 시스템이 값의 타입이 키와 같음을 보장해 줄 것이다.
Favorites
는 타입 안전 이종(heterogeneous) 컨테이너이다.
- String을 요청했는데 Integer를 반환하는 일은 절대 없다.
- 모든 키의 타입이 제각각이라, 일반적인 맵과 달리 여러 가지 타입의 원소를 담을 수 있다.
public class Favorites {
private Map<Class<?>, Object> favorites = new HashMap<>();
// 값이 아니라 키로 중첩된 와일드카드 타입을 사용하고 있다.
// 값은 Object로 선언되어 모든 값이 키로 명시된 타입임을 보증하지 않는다.
public <T> void putFavorite(Class<T> type, T instance) {
favorites.put(Objects.requireNonNull(type), type.cast(instance));
// 동적 형변환으로 타입 불변식을 어기는 일이 없도록 보장한다.
}
public <T> T getFavorite(Class<T> type) {
return type.cast(favorites.get(type));
// favorites.get()에 의해 반환되는 객체의 타입은 Object이므로,
// 이를 타입 변환해 반환해야 한다. (cast(): 동적 형변환 연산자)
}
}
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());
}
-
E, List<E>, List<String>
과 같은 실체화 불가 타입에는 사용이 불가하다. -
cast()
메서드는 제너릭을 활용해, 비검사 형변환하는 손실 없이도 클래스를 타입 안전하게 만들 수 있다.
public class Class<T> {
public T cast(Object obj) {
if (obj != null && !isInstance(obj))
throw new ClassCastException(cannotCastMsg(obj));
return (T) obj;
}
}
Favorites
가 허용하는 타입을 제한하고 싶다면, 한정적 타입 토큰을 활용하면 된다.
- 한정적 타입 매개변수(item 29)나 한정적 와일드카드(item 31)를 사용해 표현 가능한 타입을 사용하면 된다.
Leave a comment