1. 타입 안전 이종 컨테이너 패턴
매개변수화 되는 대상은 원소가 아닌 컨테이너 자신이다 Set<Integer>가 있을 때 매개변수화 되는 것은 Integer가 아니라 List<Integer>이다.
하나의 컨테이너에서 매개변수화 할 수 있는 타입의 수가 제한된다.
이보다 유연한 수단 : 타입 안전 이종 컨테이너 패턴
타입 안전 이종 컨테이너 패턴 (type safe heterogeneous container pattern)
= 컨테이너 대신 키를 매개변수화 한 다음, 컨테이너에 값을 넣거나 뺄대 매개변수화 한 키를 함께 제공한다.
각 타입의 Class 객체를 매개변수화한 키 역할로 사용한다 : 이때 class 리터럴의 타입은 Class<T>이다.
public class Favorites{ // 타입 이종 컨테이너 추상화
public <T> void putFavorite(Class<T> type, T instance);
public <T> T getFavorite(Class<T> type)
}
타입토큰 : 컴파일 타임 정보와 런타임 타입 정보를 알아내기 위해 메서드들이 주고받는 class 리터럴
public class Favorites { // 타입 이종 컨테이너 구현
private Map<Class<?>, Object> favorites = new HashMap<>();
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));
}
}
여기서 와일드 카드 타입으로 put 할수 없다고 생각할 수 있지만, 이때 키가 와일드 카드 타입이기 때문에 넣을 수 있다.
Map의 값이 Object를이기 대문에 Class의 cast 메서드를 사용해 동적 형변환한다.
이때 cast 메서드에서 제네릭의 이점을 완벽히 사용한다 : 비검사 형변환 없이도 Favorites를 타입 안전하게 한다.
public class Class<T>{
T cast(Object obj);
}
2. 타입 안전 이종 컨테이너의 제약
a. 악의적인 클라이언트가 Class 객체를 로타입으로 넘기면 Favorites 인스턴스의 타입 안정성이 쉽게 깨진다.
f.putFavorite((Class) Integer.class, "Integer의 인스턴스가 아니다.");
int favoriteInteger = f.getFavorite(Integer.class)
따라서 위에 구현한대로, put을 해줄 당시에 type.cast(instacne)와 같은 동적 형변환을 넣어주자
b. 실체화 불가 타입에는 사용할 수 없다.
List<String>용 Class 객체를 얻을 수 없기 때문이다.List.class를 사용해야하지만 이렇게 했을 때List<String>.class,List<Integer>.class모두를 허용하여 객체 참조를 한다면 오류가 많아질 것이다.
3. 한정적 타입 토큰
한정적 타입 매개변수나 한정적 와일드카드를 사용하여 표현가능한 타입을 제한하는 타입토큰
애너테이션 API는 한정적 타입 토큰을 적극적으로 사용한다.
public <T extends Annotation> T getAnnotation(Class<T> annotationType)
annotationType : 애너테이션 타입을 뜻하는 한정적 타입 토큰
대상 요소에 달려있는 애너테이션을 런타임에 읽어오는 기능을 한다.
이 메서드는 토큰으로 명시한 타입의 애너테이션이 대상 요소에 달려있으면 그 애너테이션을 반환하고, 없다면 null을 반환
즉, 애너테이션된 요소는 그 키가 애너테이션 타입인 타입 안전 이종 컨테이너인 것이다.
Class<?> 타입의 객체를 한정적 타입 토큰을 받는 메서드에 넘기고 싶을 때
asSubclass 메서드 : 호출된 인스턴스 자신의 Class 객체를 인수가 명시한 클래스로 형변환 한다.
if 성공 : 인수로 받은 클래스 객체를 반환
else : ClassCastException
static Annotation getAnnotation(AnnotationElement 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))
}
'Dev Book Review > Effective Java' 카테고리의 다른 글
| [Effective Java] item 34. int 상수 대신 열거 타입을 사용하라 (2) | 2020.06.27 |
|---|---|
| [Effective Java] Chapter 5: 제네릭 (0) | 2020.05.19 |
| [Effective Java] item32. 제네릭과 가변인수를 함께 쓸 때는 신중하라 (0) | 2020.05.19 |
| [Effective Java] item31. 한정적 와일드 카드를 사용해 API 유연성을 높여라 (0) | 2020.05.19 |
| [Effective Java] item30. 이왕이면 제네릭 메서드로 만들라 (0) | 2020.05.19 |