Neal Gafter's Super Type Tokens

Neal Gafter’s Super Type Tokens

jdk 5 generics 추가 되면서 java.lang.Class를 제네릭 타입으로 바꿀수 있다 예를 들면 String.class를 지금은 Class으로
Joshua Bloch 말하는 THC, or Typesafe Heterogenous Container pattern이다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

package com.github.sejoung.codetest.generics.nealgafter;

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

public class Favorites {
private Map<Class<?>, Object> favorites = new HashMap<Class<?>, Object>();

public <T> void setFavorite(Class<T> klass, T thing) {
favorites.put(klass, thing);
}

public <T> T getFavorite(Class<T> klass) {
return klass.cast(favorites.get(klass));
}

public static void main(String[] args) {
Favorites f = new Favorites();
f.setFavorite(String.class, "Java");
f.setFavorite(Integer.class, 0xcafebabe);
String s = f.getFavorite(String.class);
int i = f.getFavorite(Integer.class);
System.out.println(s);
System.out.println(i);
}
}


위와 같은 코드를 타입 안전하고 컴파일시에 문제에 대해 판단할수 있다.
하지만 위에 코드는 제약이 있음니다.

아래의 코드를 추가하면 이레이져에 의해 문제가 됩니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34

package com.github.sejoung.codetest.generics.nealgafter;

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

public class Favorites {
private Map<Class<?>, Object> favorites = new HashMap<Class<?>, Object>();

public <T> void setFavorite(Class<T> klass, T thing) {
favorites.put(klass, thing);
}

public <T> T getFavorite(Class<T> klass) {
return klass.cast(favorites.get(klass));
}

public static void main(String[] args) {
Favorites f = new Favorites();
f.setFavorite(String.class, "Java");
f.setFavorite(Integer.class, 0xcafebabe);
f.setFavorite (List<String>.class, Collections.emptyList ());

String s = f.getFavorite(String.class);
int i = f.getFavorite(Integer.class);

System.out.println(s);
System.out.println(i);
}
}


컴파일 메시지

1
2
3
4
5
6
7
Error:(23, 37) java: <identifier> expected
Error:(23, 42) java: <identifier> expected
Error:(28, 27) java: <identifier> expected
Error:(28, 29) java: <identifier> expected
Error:(29, 27) java: <identifier> expected
Error:(29, 29) java: <identifier> expected
Error:(31, 2) java: reached end of file while parsing

위에 문제를 해결하기 위해 아래와 같은 방법을 사용하였다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58

package com.github.sejoung.codetest.generics.nealgafter;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;

/**
* References a generic type.
*
* @author crazybob@google.com (Bob Lee)
*/
public abstract class TypeReference<T> {

private final Type type;
private volatile Constructor<?> constructor;

protected TypeReference() {
Type superclass = getClass().getGenericSuperclass();
if (superclass instanceof Class) {
throw new RuntimeException("Missing type parameter.");
}
this.type = ((ParameterizedType) superclass).getActualTypeArguments()[0];
}

/**
* Instantiates a new instance of {@code T} using the default, no-arg
* constructor.
*/
@SuppressWarnings("unchecked")
public T newInstance()
throws NoSuchMethodException, IllegalAccessException,
InvocationTargetException, InstantiationException {
if (constructor == null) {
Class<?> rawType = type instanceof Class<?>
? (Class<?>) type
: (Class<?>) ((ParameterizedType) type).getRawType();
constructor = rawType.getConstructor();
}
return (T) constructor.newInstance();
}

/**
* Gets the referenced type.
*/
public Type getType() {
return this.type;
}

public static void main(String[] args) throws Exception {
List<String> l1 = new TypeReference<ArrayList<String>>(){}.newInstance();
List l2 = new TypeReference<ArrayList>() {}.newInstance();
}
}

참조