컨테이너 대신 키를 매개변수화한 다음, 컨테이너에 값을 넣거나 뺄 때 매개변수화한 키를 함께 제공하여 제네릭 타입 시스템이 값의 타입이 키와 같음을 보장해주는 설계 패턴 방식
public class Favorites {
private Map<Class<?>, Object> favorites = new HashMap<>();
public <T> T getFavorite(Class<T> type) {
return type.cast(favorites.get(type));
}
public <T> void putFavorite(Class<T> type, T instance) {
favorites.put(Objects.requireNonNull(type), type.cast(instance));
}
...
}
Class
가 아닌 Class<T>
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());
}
Favorites 클래스를 사용하는 클라이언트 코드
Favorites 인스턴스는 타입 안전함
⇒ Favorites는 타입 안전 이종 컨테이너
클라이언트에서 악의적으로 로 타입을 넘기면 안정성이 깨짐
f.putFavorite((Class) List.class, "악의적이다");
// 런타임에 ClassCastException 발생
→ 이를 예방하기 위해 형변환 검사를 추가하자
실체화 불가 타입에는 사용할 수 없음
ex) List<String>.class 와 같은 실체화 불가 타입에 사용할 수 없음
→ 이를 해결하기 위해 슈퍼 타입 토근 이라는 방식이 있지만 완벽하지 않음
한정적 타입 토큰을 활용하면 getFavorite, putFavorite 메서드가 허용하는 타입을 제한할 수 있다.
<aside> 🏷️
한정적 타입 토큰?
한정적 타입 매개변수(E extends Delayed)나 한정적 와일드카드(? extends Delayed)을 사용하여 표현 가능한 타입을 제한하는 토큰
</aside>
예시
// 대상 요소에 달려있는 애너테이션을 런타임에 읽어 오는 기능
public <T extends Annotation> T getAnnotation(Class<T> annotationType);
이 메서드는 토큰으로 명시한 타입의 애너테이션이 대상 요소에 달려 있다면 그 애너테이션을 반환하고, 없다면 Null을 반환 (타입 안전 이종 컨테이너)
T
가 Annotation
을 상속하는 타입으로 제한됨
→ 메서드가 반환하는 타입의 안전성을 보장
→ 즉, 호출자가 어떤 애너테이션을 요청하든, 그 애너테이션이 Annotation
의 서브타입일 것이라는 것을 컴파일 타임에 보장할 수 있음