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

무한성장개발자·2024년 10월 20일

아이템 33. 핵심 정리 - 타입 토큰을 사용한 타입 안전 이종 컨테이너

package me.whiteship.chapter05.item33.type_token;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;

public class Favorites<T> {
    List<T> value;

//    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<String> names = new Favorites<>();
        names.value.add("whiteship");
        names.value.add(1);

        Favorites favorites = new Favorites();
        favorites.put(String.class, "keesun");
        favorites.put(Integer.class, 2);

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

}
package me.whiteship.chapter05.item33.type_token;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;

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 void put(Class clazz, Object value) {
        this.map.put(Objects.requireNonNull(clazz), clazz.cast(value));
    }

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

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


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

}
package me.whiteship.chapter05.item33.type_token;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;

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> void put(Class<T> clazz, T value) {
        this.map.put(clazz, value);
    }

    @SuppressWarnings("unchecked")
    public <T> T get(Class<T> clazz) {
        return (T)this.map.get(clazz);
    }

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


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

}
package me.whiteship.chapter05.item33.type_token;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;

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> void put(Class<T> clazz, T value) {
        this.map.put(clazz, 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);
        String s = favorites.get(String.class);
        Integer integer = favorites.get(Integer.class);


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

}

clazz.cast로 하면 get을 할 때 해당 타입으로 나오게 된다.

package me.whiteship.chapter05.item33.type_token;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;

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> void put(Class<T> clazz, T value) {
        this.map.put(Objects.requireNonNull(clazz), 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((Class)String.class, 1);

        favorites.put(String.class, "keesun");
        favorites.put(Integer.class, 2);
        String s = favorites.get(String.class);
        Integer integer = favorites.get(Integer.class);


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

}

이 경우 문자열을 꺼낼 때 내가 요청했던 타입으로 숫자를 Integer로 형변환 하려고 한다. 그 후에 ClassCastException이 발생한다.

넣을 때 오류가 나온다.

![](https://velog.velcdn.com/images/readingdeveloper/post/c5d0f84c-81a0-42f0-9a28-401c157bd0e6/image.png)

덮어쓰게 된다.

List<Integer>는 문법이 허용하지 않는다.

아이템 33. 완벽 공략 - 수퍼 타입 토큰

package me.whiteship.chapter05.item33.super_type_token;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;

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

    }
}

 ParameterizedType parameterizedType = (ParameterizedType) type; 은 전부 Super<String>을 의미하는 것이다.

상속을 쓴 경우 sub로부터 알아낼 수 있다.

익명 상속 클래스를 사용할 수 있다.

package me.whiteship.chapter05.item33.super_type_token;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;

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;
    }
}
package me.whiteship.chapter05.item33.super_type_token;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

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

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

package me.whiteship.chapter05.item33.super_type_token;

import java.util.ArrayList;
import java.util.List;

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

슈퍼타입토큰 제약사항이다.
equals에 같다. ls와 li가 hashcode가 같기 때문에 ls에 비어있어야 하는데 1이 들어가 있다.

아이템 33. 핵심 정리 - 한정적 타입 토큰

package me.whiteship.chapter05.item33.bounded_type_token;

import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;

// 코드 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()));
    }
}

AnnotatedElement는 Annotation을 가지고 있는 모든 것을 의미하는 것이다.

asSubclass라는 것은 이 뒤에 전달하는 type의 하위타입으로 변환을 해준다.

Class<? extends Annotation> subclass = annotationType.asSubclass(Annotation.class);
package me.whiteship.chapter05.item33.bounded_type_token;

import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;

// 코드 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);
        }
        Class<? extends Annotation> subclass = annotationType.asSubclass(Annotation.class);
        return element.getAnnotation(subclass);
    }

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

0개의 댓글