(시청일 : 20170924)


- https://www.youtube.com/watch?v=01sdXvZSjcI : 스프링에서 사용되는 ParameterizedTypeReference의 작동 원리인 Super Type Token의 동작 원리와 활용법을 알아봅니다.




■ Type Token : 타입 정보를 key로 넘겨서 value를 리턴


■ Type Safe Token 예제 코드

public class TypeToken {
    static Class TypeSafeMap {
        Map<Class<?>, Object> map = new HashMap<>();
        <T> void put(Class<T> clazz, T value) {
            map.put(clazz, value);
        }
        <T> T get(Class<T> clazz) {
            return clazz.cast(map.get(clazz));
        }
    }
    public static void main(String[] args) throws Exception {
        TypeSafeMap m = new TypeSafeMap();
        m.put(Integer.class, 1);
        m.put(String.class, "String");
        m.put(List.class, Arrays.asList(1,2,3));
        System.out.println(m.get(Integer.class));
        System.out.println(m.get(String.class));
        System.out.println(m.get(List.class));
    }
}




■ Super Type Token 예제 코드   => 메모리 누수를 야기하는 등 좋지 않은 코드라서, 2.5회에서 개선 됨.

public class SuperTypeToken {
    static Class TypeSafeMap {
        Map<TypeReference<?>, Object> map = new HashMap<>();
    <T> void put(TypeReference<T> tr, T value) {
        map.put(tr, value);
    }
    <T> T get(TypeReference<T> tr) {
        if (tr.type instanceof Class<?>)
            return ((Class<T>)tr.type).cast(map.get(tr));
        else
            return ((Class<T>)((ParameterizedType)tr.type).getRawType()).cast(map.get(tr)); // TR<List<String>>
    }
}
    static class TypeReference<T> {
        Type type;
        public TypeReference() {
            Type stype = getClass().getGenericSuperclass();
            if (stype instanceof ParameterizedType) {
                this.type = ((ParameterizedType) stype).getActualTypeArguments()[0];
            } else throw new RuntimeException();
        }
        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass().getGenericSuperclass() != o.getClass().getGenericSuperclass()) return false;
            TypeReference that = (TypeReference) o;
            return type.equals(that.type);
        }
        @Override
        public int hashCode() {
            return type.hashCode();
        }
    }
   public static void main(String[] args) throws Exception {
        TypeReference m = new TypeSafeMap();
        m.put(new TypeReference<Integer>() {}, 1);
        m.put(new TypeReference<String>() {}, "String");
        m.put(new TypeReference<List<Integer>>() {}, Arrays.asList(1,2,3)); // List<Integer>
        m.put(new TypeReference<List<String>>() {}, Arrays.asList("a","b","c")); // List<String>
        m.put(new TypeReference<List<List<String>>>() {}, Arrays.asList(Arrays.asList("a"),Arrays.asList("b"),Arrays.asList("c"))); // List<String>

        System.out.println(m.get(new TypeReference<Integer>() {}));
        System.out.println(m.get(new TypeReference<String>() {}));
        System.out.println(m.get(new TypeReference<List<Integer>>() {}));
        System.out.println(m.get(new TypeReference<List<String>>() {}));
        System.out.println(m.get(new TypeReference<List<List<String>>>() {}));
}
}




■ 위의 코드와 유사한 Spring 3.2 버전 이상에서 지원하는
ParameterizedTypeReference의 사용 예제
public class SpringTypeReference {
    public static void main(String[] args) {
        ParameterizedTypeReference<?> typeRef = new ParameterizedTypeReference<List<Map<Set<Integer>, String>>>() {};
        System.out.println(typeRef.getType());
    }
}
 



■ ParameterizedTypeReference의 사용 사례

@SpringBootApplication
public class TobyTv002liveApplication {
    public static void main(String[] args) {
        SpringApplication.run(TobyTv002liveApplication.class, args);
    }
    @RestController
    public static class MyController {
        @RequestMapping("/")
        public List<User> users() {
            return Arrays.asList(new User("A"), new User("B"), new User("C"));
        }
    }
    public static class User {
        String name;
        public User(String name) {
            this.name = name;
        }
        public User() {
        }
        public String getName() {
            return name;
        }
        @Override
        public String toString() {
            return "User[" +
                    "name='" + name + '\'' +
                    ']';
        }
    }
}

public class SpringTypeReference {
    public static void main(String[] args) {
        RestTemplate rt = new RestTemplate();
        // 타입 safe하지 않음
//        List<Map> users = rt.getForObject("http://localhost:8080";, List.class);
//        System.out.println(users.get(0).getName("name"));
        List<User> users = rt.exchange("http://localhost:8080";,
                HttpMethod.Get, null, new ParameterizedTypeReference<List<User>>() {}).getBody();
        users.forEach(System.out::println );
    }
}


(시청일 : 20171001)


https://www.youtube.com/watch?v=y_uGSqpE4So : 2회에서 설명했던 수퍼 타입 토큰의 예제를 좀 더 효율적으로 개선하는 방법과 스프링이 제공하는 ResolvableType 사용법을 간단히 설명합니다.


■ Super Type Token (2회로 부터 개선된 버전)
 
public class SuperTypeToken {
    static Class TypeSafeMap {
        Map<Type, Object> map = new HashMap<>();
    <T> void put(TypeReference<T> tr, T value) {
        map.put(tr.type, value);
    }
    <T> T get(TypeReference<T> tr) {
        if (tr.type instanceof Class<?>)
            return ((Class<T>)tr.type).cast(map.get(tr.type));
        else
            return ((Class<T>)((ParameterizedType)tr.type).getRawType()).cast(map.get(tr.type)); // TR<List<String>>
    }
}
    static class TypeReference<T> {
        Type type;
        public TypeReference() {
            Type stype = getClass().getGenericSuperclass();
            if (stype instanceof ParameterizedType) {
                this.type = ((ParameterizedType) stype).getActualTypeArguments()[0];
            } else throw new RuntimeException();
        }
    }
    public static void main(String[] args) throws Exception {
        TypeReference m = new TypeSafeMap();
        m.put(new TypeReference<Integer>() {}, 1);
        m.put(new TypeReference<String>() {}, "String");
        m.put(new TypeReference<List<Integer>>() {}, Arrays.asList(1,2,3)); // List<Integer>
        m.put(new TypeReference<List<String>>() {}, Arrays.asList("a","b","c")); // List<String>
        m.put(new TypeReference<List<List<String>>>() {}, Arrays.asList(Arrays.asList("a","b"),Arrays.asList("c","d"),Arrays.asList("e"))); // List<String>

        System.out.println(m.get(new TypeReference<Integer>() {})); // 1
        System.out.println(m.get(new TypeReference<String>() {})); // String
        System.out.println(m.get(new TypeReference<List<Integer>>() {})); // [1, 2, 3]
        System.out.println(m.get(new TypeReference<List<String>>() {})); // [a, b, c]
        System.out.println(m.get(new TypeReference<List<List<String>>>() {})); // [[a, b], [c, d], [e]]

        Map<String, String> map = new HashMap<>();
        map.put("key1", "value1");
        map.put("key2", "value2");
        m.put(new TypeReference<Map<String, Stirng>>(), map);  // {key1=value1, key2=value2}
    }
}



■ 스프링 4.0버전 이상에서 지원하는 ResolvableType 예제 코드

public class SuperTypeToken {
    static class TypeReference<T> {
        Type type;
        public TypeReference() {
            Type stype = getClass().getGenericSuperclass();
            if (stype instanceof ParameterizedType) {
                this.type = ((ParameterizedType) stype).getActualTypeArguments()[0];
            } else throw new RuntimeException();
        }
    }
    public static void main(String[] args) throws Exception {
        //ResolvableType rt = ResolvableType.forClass(TypeReference.class);

        ResolvableType rt = ResolvableType.forInstance(new TypeReference<List<String>>() {});
        
        System.out.println(rt.getSuperType().getGenerics(0).getType()); // java.Util.List<java.lang.String>
        System.out.println(rt.getSuperType().getGenerics(0).getNested(2).getType()); // class.java.lang.String
        System.out.println(rt.getSuperType().hasUnresolvableGenerics()); // false
        System.out.println(ResolvableType.forInstance(new ArrayList<String>()).hasUnresolvableGenerics()); // true
    }
}


+ Recent posts