
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이 발생한다.

넣을 때 오류가 나온다.


덮어쓰게 된다.

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

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이 들어가 있다.


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