아이템 33 - 타입 안전 이종 컨테이너를 고려하라.
JAVA ·제네릭은 Set< E >, Map<K,V> 등의 컬렉션과 ThreadLocal< T >, AtomicReference< T > 등의 단일원소 컨테이너에도 흔히 쓰인다. 이런 모든 쓰임에서 매개변수화되는 대상은 원소가 아닌 컨테이너 자신이다. 따라서 하나의 컨테이너에서 매개변수화할 수 있는 타입의 수가 제한된다.
하지만 더 유연한 수단이 필요할 때가 있다.
컨테이너 대신 키를 매개변수화한 다음, 컨테이너에 값을 넣거나 뺄 때 매개변수화한 키를 함께 제공하면 제네릭 타입 시스템이 값의 타입이 키와 같음을 보장해줄 것이다. 이런한 설계 방식을 타입 안전 이종 컨테이너 패턴이라 한다.
타입 토큰을 사용한 타입 안전 이종 컨테이너
- 타입 안전 이종 컨테이너: 한 타입의 객체만 담을 수 있는 컨테이너가 아니라 여러 다른 타입(이종)을 담을 수 있는 타입 안전한 컨테이너
- 타입 토큰: 컴파일타임 타입 정보와 런타임 타입 정보를 알아내기 위해 메서드들이 주고받는 class 리터럴
- eg) String.class 또는 Class
- eg) String.class 또는 Class
- 타입 안전 이종 컨테이너 구현 방법:컨테이너가 아니라 “키”를 매개변수화 하라!
- eg) ```java public class Favorites { private Map<Class<?>, Object> favorites = new HashMap<>();
public
void putFavorite(Class type, T instance) { favorite.put(Objects.requireNonNull(type), instance); } public
T getFavorite(Class type) { return type.cast(favorites.get(type)); } } ``` - 위 클레스의 제약 두 가지
- 첫 번째, 악의적인 클라이언트가 Class 객체를 제네릭이 아닌 로타입으로 넘기면 인스턴스의 타입 안정성이 쉽게 깨진다.
- 타입 안정성을 보장하려면? putFavorite 메서드에서 인수로 주어진 instance의 타입이 type으로 명시한 타입과 같은지 확인하면 된다.
- instance -> type.cast(instance)
- 타입 안정성을 보장하려면? putFavorite 메서드에서 인수로 주어진 instance의 타입이 type으로 명시한 타입과 같은지 확인하면 된다.
- 두 번째, 실체화 불가 타입에는 사용할 수 없다.
- 다시 말해, String과 String[]은 저장할 수 있어도 List
은 저장할 수 없다. - List< String >용 Class 객체를 얻을 수 없기 때문이다.
- 슈퍼 타입 토큰으로 해결할 수 있다.
- 다시 말해, String과 String[]은 저장할 수 있어도 List
- 첫 번째, 악의적인 클라이언트가 Class 객체를 제네릭이 아닌 로타입으로 넘기면 인스턴스의 타입 안정성이 쉽게 깨진다.
한정적 타입 토큰
- 한정적 타입 토큰을 사용한다면, 이종 컨테이너에 사용할 수 있는 타입을 제한할 수 있다.
- eg) AnnotatedElement.< T extends Annotation > T getAnnotation(Class< T > annotationClass);
- asSubclass 메서드
- 메서드를 호출하는 class 인스턴스를 인수로 명시한 클래스로 안전하게 형변환 한다.
- 형변환된다는 것은 이 클래스가 인수로 명시한 클래스의 하위 클래스라는 뜻
- eg)
static Annotation getAnnotation(AnnotatedElement element, String annotationTypeName) { Class<?> annotationType = null; try { annotationType = Class.forName(annotationTypeName); } catch(Exception ex) { throw new IllegalArgumentException(ex); } return element.getAnnotation(annotationType.asSubclass(Annotation.class)); }
- 메서드를 호출하는 class 인스턴스를 인수로 명시한 클래스로 안전하게 형변환 한다.