- Set, Map<K, V>, ThreadLocal 와 같은 단일 원소 컨테이너
- 컨테이너 자체가 매개변수화 되었다!
public class Favorites {
public <T> void putFavorite(Class<T> type, T instance);
public <T> T getFavorite(Class<T> type);
}
- 컨테이너 대신 키를 매개변수화 한다.
- 여러 가지 타입의 원소를 담을 수 있다.
- 제네릭 타입을 통해 값(value)이 키(key)와 같은 타입인 것이 보장된다!
public class Favorites {
private Map<Class<?>, Object> favorites = new HashMap<>();
public <T> void putFavorite(Class<T> type, T instance) {
favorites.put(Objects.requireNonNull(type), instance);
}
public <T> T getFavorite(Class<T> type) {
return type.cast(favorites.get(type));
}
}
- Class가 비한정적 와일드카드 타입이라 아무것도 넣을 수 없을 것 같지만, Class는 key이기 때문에 넣을 수 있다!
- type과 instance의
타입 링크
정보는 버려지지만, getFavorite()을 통해 되살아난다. - type.cast()는 ClassCastException을 던질 수 있지만, 우리는 클래스가 항상 일치함을 알고 있다.
f.putFavorite((Class)Integer.class, "String");
int favoriteInteger = f.getFavorite(Integer.class);
[Throw ClassCastException]
public <T> void putFavorite(Class<T> type, T instance) {
favorites.put(Objects.requireNonNull(type), type.cast(instance));
}
- 동적 형변환으로 런타임 안정성 확보할 수 있다.
- 예시) java.util.Collections의 checkedSet, checkedList, checkedMap
- List.class == List.class == List.class
슈퍼 타입 토큰
으로 해결할 수도 있지만 완벽하지 않으니 주의해서 사용할 것!- 만족스러운 우회로(해결법)은 없다. (조슈아 블로크 주장)
public <T extends Annotation> T getAnnotation(Class<T> annotationType);
- AnnotatedElement 인터페이스는 리플렉션으로 대상에 달려있는 어노테이션을 가져온다.
- annotationType을 키로 갖는 타입 안전 이종 컨테이너 형태로 동작한다.
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<?> 타입의 객체를 위와 같은 한정적 타입 토큰으로 형변환이 필요할 때
- 비검사 형변환보다 안전한 Class.asSubclass() 메소드!
닐 게프터 블로그 : https://gafter.blogspot.com/2006/12/super-type-tokens.html
public class SuperTypeToken {
static class Sup<T> {
T value;
}
public static void main(String[] args) throws Exception {
Sup<String> s = new Sup<>();
System.out.println(s.getClass().getDeclaredField("value").getType()); // class java.lang.Object
}
}
public class SuperTypeToken {
static class Sup<T> {
T value;
}
static class Sub extends Sup<Map<List<?>, Set<String>>> {
}
public static void main(String[] args) throws Exception {
Sub b = new Sub();
Type t = b.getClass().getGenericSuperclass();
ParameterizedType pType = (ParameterizedType)t;
System.out.println(pType.getActualTypeArguments()[0]); // java.util.Map<java.util.List<?>, java.util.Set<java.lang.String>>
}
}
public class SuperTypeToken {
static class TypeReference<T> {
Type type;
public TypeReference() {
Type sType = getClass().getGenericSuperclass();
if (sType instanceof ParameterizedType) {
this.type = ((ParameterizedType)sType).getActualTypeArguments()[0];
} else {
throw new RuntimeException();
}
}
}
public static void main(String[] args) throws Exception {
// TypeReference를 상속받은 익명 클래스를 이용하기 때문에 바디부분{}이 필요하다.
TypeReference t = new TypeReference<List<String>>(){};
System.out.println(t.type); // java.util.List<java.lang.String>
}
}