-
아이템33. 타입 안전 이종 컨테이너를 고려하라.Java/이펙티브 자바 2023. 11. 9. 15:22
핵심정리 - 타입 토큰을 사용한 타입 안전 이종 컨테이너
-타입 안전 이종 컨테이너: 한 타입의 객체만 담을 수 있는 컨테이너가 아니라 여러 다른 타입 (이종)을 담을 수 있는 타입 안전한 컨테이너.
- 타입 토큰: String.class 또는 Class<String>
- 타입 안전 이종 컨테이너 구현 방법: 컨테이너가 아니라 "키"를 매개변수화 하라!
public class Favorites { private Map<Class<?>, Object> favorites = new HashMap<>(); public <T> void putFavorite(Class<T> type, T instance) { favorites.put(Objects.requireNotNull(type), instance); } public <T> T getFavorite(Class<T> type) { return type.cast(favorites.get(type)); } }
public static void main(String[] args) { Favorites favorites = new Favorites(); // favorites.put(String.class, 1); //compile error favorites.put(String.class, "test"); favorites.put(Integer.class, 1); String s = favorites.get(String.class); Integer integer = favorites.get(Integer.class); }
완벽공략 - 수퍼타입 토큰
익명 클래스와 제네릭 클래스 상속을 사용한 타입 토큰
- 닐 게프터의 슈퍼 타입 토큰
- 상속을 사용한 경우 제네릭 타입을 알아낼 수 있다. 이 경우에는 제네릭 타입이 제거되지 않기 때문에..
static class Super<T> { T value; } static class Sub extends Super<String> { // 상속받는 경우에는 상속정보가 남는다. } public static void main(String[] args) throws NoSuchFieldException { Super<String> stringSuper = new Super<>(); System.out.println(stringSuper.getClass().getDeclaredField("value").getType()); //object Sub sub = new Sub(); Type type = Sub.class.getGenericSuperclass(); ParameterizedType ptype = (ParameterizedType) type; System.out.println(ptype.getActualTypeArguments()[0]); // string }
- 위의 특징을 활용해 추상클래스를 만들어준다.
public abstract class TypeRef<T> { private final Type type; protected TypeRef() { ParameterizedType superclass = (ParameterizedType) getClass().getGenericSuperclass(); type = superclass.getActualTypeArguments()[0]; } public Type getType() { return type; } @Override public boolean equals(Object o) { return o instanceof TypeRef && ((TypeRef)o).type.equals(type); } @Override public int hashCode() { return type.hashCode(); } }
- 위 추상클래스를 활용해 List<?> .class 도 활용가능하다!
public class Favorites2 { private final Map<TypeRef<?>, Object> favorites = new HashMap<>(); public <T> void put(TypeRef<T> typeRef, T thing) { favorites.put(typeRef, thing); } public <T> T get(TypeRef<T> typeRef) { return (T) favorites.get(typeRef); } public static void main(String[] args) { Favorites2 f = new Favorites2(); f.put(new TypeRef<List<String>>() {}, List.of("a", "b", "c")); f.put(new TypeRef<List<Integer>>() {}, List.of(1, 2, 3)); f.get(new TypeRef<List<String>>() {}).forEach(System.out::println); f.get(new TypeRef<List<Integer>>() {}).forEach(System.out::println); } }
핵심정리 - 한정적 타입 토큰
- 한정적 타입 토큰을 사용한다면, 이종 컨테이너에 사용할 수 있는 타입을 제한할 수 있다.
ㆍAnnotatedElement.<T extends Annotation> T
getAnnotation(Class<T> annotationClass);
- asSubclass 메서드
ㆍ메서드를 호출하는 Class 인스턴스를 인수로 명시한 클래스로 형변환한다.
static Annotation getAnnotaion(AnnotatedElement element, String annotationTypeName) { Class<?> annotationType = null; // 비한정적 타입 토큰 try { annotationType = Class.forName(annotationTypeName); } catch (Exception ex) { throw new IllegalArgumentException(ex); } Class<? extends Annotation> subclass = annotationType.asSubclass(Annotation.class); return element.getAnnotation(subclass); }
'Java > 이펙티브 자바' 카테고리의 다른 글
아이템 32. 제네릭과 가변인수를 함께 쓸 때는 신중하라. (0) 2023.11.05 아이템31. 한정적 와일드카드를 사용해 API 유연성을 높이라 (0) 2023.10.31 아이템29 & 아이템30 (0) 2023.09.28 아이템28. 배열보다는 리스트를 사용하라 (0) 2023.09.23 아이템 27. 비검사 경고를 제거하라 (0) 2023.09.21