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 |