타입 안전 이종 컨테이너 패턴(type sage heterogeneous container pattern) : 컨테이너 대신 키를 매개변수화한 다음, 컨테이너에 값을 넣거나 뺄 때 매개변수화된 키를 함께 제공하는 방식. 이런경우 제네릭 타입 시스템이 값의 타입이 키와 같음을 보장해 준다.
예시1) Favorites class (타입별로 즐겨 찾는 인스턴스 저장 후 검색)
Class
가 아닌 Class<T>
이다.String.class
의 타입은 Class<String>
이다.Integer.class
의 타입은 Class<integer>
이다.타입 토큰(type token) : 컴파일타임 타입 정보와 런타임 타입 정보를 알아내기 위해 메서드들이 주고받는 class 리터럴
public static void main(String[] args) {
Favorites f = new Favorites();
f.putFavorite(String.class, "Java");
f.putFavorite(Integer.class, 0x123abcd);
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
인스턴스는 타입 안전하다. String
요청 시 Integer
반환하지 않는다 즉 타입 안전 이종 컨테이너 이다.
public class Favorites {
private Map<Class<?>, Object> favorite = new HashMap<>();
public <T> void putFavorite(Class<T> type, T instance) {
favorite.put(Objects.requireNonNull(type), instance);
}
public <T> T getFavorite(Class<T> type) {
return type.cast(favorite.get(type));
}
}
Map<Class<?>, Object>
는 키가 와일드카드 타입니다.Object
이다. Put
메서드는 <T>
를 통해 Object
가 Class<?>
와 같은 타임임을 보장한다.Class<?>
와 Object
관계에 포함되는것이다.(하지만 코드 설계자는 Put
메서드를 통해 타입이 같음을 알 수 있다.get
메서드는 주어진 Class
객체에 해당하는 값을 꺼낸다. 반환 객체의 타입은 Object
로 잘못된 컴파일타임 타입을 가지고 있다.T
로 바꿔 반환해야 한다.Class
의 cast
메서드를 사용해 객체 참조를 Class
객체가 가리키는 타입으로 동적 형변환 해준다.T
로 비검사 형변환하는 손실 없이도 Favorites
를 타입 안전하게 만들 수 있다.악의적인 클라이언트가 Class 객체를 로 타입으로 넘기면 타입 안전성이 깨진다. 하지만 컴파일 시 비검사 경고가 뜰것이다.
실체화 불가 타입에는 사용할 수 없다.
String
이나 String[]
은 저장 가능하지만 List<String>
은 저장불가하다.(컴파일 되지 않는다)List<String>
용 Class
객체를 얻을 수 없기 떄문이다.List<String>.class
작성 시 문법 오류가 난다.List<String>
과 List<Integer>
는 List.class
라는 같은 Class
를 공유하므로 만약 실체화 불가 타입을 허용한다면 키값이 중볻되는 아수라장이 될것이다.Favorites 가 사용하는 타입 토큰은 비한정적이다.
이를 한정적 타입 토큰으로 제한할 수 있다.
public <T extends Annotation> T getAnNotation(Class<T> annotationType);
<T>
가 아닌 <T extends Annotation>
으로 제한을 준다.만약 Class<?> 타입의 객체가 있고, 이를 한정적 타입 토큰을 받는 메서드에 넘기려면 어떻게 해야 할까?
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);
);
}
이펙티브 자바 - 조슈아 블로크
https://absorbed-cheek-029.notion.site/item33-009fd935813c4247a028f34a5ba522b0