tonykang22 / study

0 stars 0 forks source link

[이펙티브자바] 아이템 33. 타입 안전 이종 컨테이너를 고려하라. #157

Open leeyuunsung opened 1 year ago

leeyuunsung commented 1 year ago

아이템 33. 타입 안전 이종 컨테이너를 고려하라.

핵심 정리: 타입 토큰을 사용한 타입 안전 이종 컨테이너

public static class UnsafeFavorites {
        private Map<Class, Object> map = new HashMap<>();

        public void put(Class clazz, Object value) {
            this.map.put(clazz, value);
        }

        public Object get(Class clazz) {
            return this.map.get(clazz);
        }

        public static void main(String[] args) {
            UnsafeFavorites unsafeFavorites = new UnsafeFavorites();
            unsafeFavorites.put(String.class, 2); // unsafe!!
        }

    }
public class Favorites {

    private Map<Class<?>, Object> map = new HashMap<>();

    public <T> void put(Class<T> clazz, T value) {
        this.map.put(Objects.requireNonNull(clazz), clazz.cast(value));
    }

    public <T> T get(Class<T> clazz) {
        return clazz.cast(this.map.get(clazz));
    }

    public static void main(String[] args) {
        Favorites favorites = new Favorites();
        favorites.put(String.class, "keesun");
        favorites.put(Integer.class, 2);

//        아래 둘은 구분할 수 있는 방법이 없음. List 는 모두 List 다
//        favorites.put(List<Integer>.class, List.of(1, 2, 3));
//        favorites.put(List<String>.class, List.of("a", "b", "c"));

//        List list = favorites.get(List.class);
//        list.forEach(System.out::println);
    }
}
leeyuunsung commented 1 year ago

완벽 공략 47. 수퍼 타입 토큰

익명 클래스와 제네릭 클래스 상속을 사용한 타입 토큰

닐 게프터의 슈퍼 타입 토큰

public class GenericTypeInfer {

    static class Super<T> {
        T value;
    }

    public static void main(String[] args) throws NoSuchFieldException {
        Super<String> stringSuper = new Super<>();
        System.out.println(stringSuper.getClass().getDeclaredField("value").getType());

        Type type = (new Super<String>(){}).getClass().getGenericSuperclass();
        ParameterizedType pType = (ParameterizedType) type;
        Type actualTypeArgument = pType.getActualTypeArguments()[0];
        System.out.println(actualTypeArgument);

    }
}
public abstract class TypeRef<T> {
    private final Type type;

    protected TypeRef() {
        ParameterizedType superclass = (ParameterizedType) getClass().getGenericSuperclass();
        type = superclass.getActualTypeArguments()[0];
    }

    @Override
    public boolean equals(Object o) {
        return o instanceof TypeRef && ((TypeRef)o).type.equals(type);
    }

    @Override
    public int hashCode() {
        return type.hashCode();
    }

    public Type getType() {
        return type;
    }
}
public class Favorites2 {

    private final Map<TypeRef<?>, Object> favorites = new HashMap<>();

    public <T> void put(TypeRef<T> typeRef, T thing) {
        favorites.put(typeRef, thing);
    }

    @SuppressWarnings("unchecked")
    public <T> T get(TypeRef<T> typeRref) {
        return (T)(favorites.get(typeRref));
    }

    public static void main(String[] args) {
        Favorites2 f = new Favorites2();

        TypeRef<List<String>> stringTypeRef = new TypeRef<>() {};
        System.out.println(stringTypeRef.getType());

        TypeRef<List<Integer>> integerTypeRef = new TypeRef<>() {};
        System.out.println(integerTypeRef.getType());

        f.put(stringTypeRef, List.of("a", "b", "c"));
        f.put(integerTypeRef, List.of(1, 2, 3));
        f.get(stringTypeRef).forEach(System.out::println);
        f.get(integerTypeRef).forEach(System.out::println);
    }
}

단, TypeRef 도 아래와 같은 상황처럼 우회해서 사용하게 될 경우 type cast exception 이 발생한다

class Oops {
    static Favorites2 f = new Favorites2();

    static <T> List<T> favoriteList() {
        TypeRef<List<T>> ref = new TypeRef<>() {};
        System.out.println(ref.getType());

        List<T> result = f.get(ref);
        if (result == null) {
            result = new ArrayList<T>();
            f.put(ref, result);
        }
        return result;
    }

    public static void main(String[] args) {
        List<String> ls = favoriteList();

        List<Integer> li = favoriteList();
        li.add(1);

        for (String s : ls) System.out.println(s);
    }
}
leeyuunsung commented 1 year ago

핵심 정리: 한정적 타입 토큰

// 코드 33-5 asSubclass를 사용해 한정적 타입 토큰을 안전하게 형변환한다. (204쪽)
public class PrintAnnotation {

    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));
    }

    // 명시한 클래스의 명시한 애너테이션을 출력하는 테스트 프로그램
    public static void main(String[] args) throws Exception {
        System.out.println(getAnnotation(MyService.class, FindMe.class.getName()));
    }
}