ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 아이템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);
    }

     

     

    댓글

Designed by Tistory.