Generic
Type Tokenํ์ ์ ์์ ์ฑ์ ํ๋ณดํ๊ธฐ ์ํ์ฌ ํด๋์ค์ ์๋ณ ํ ํฐ์ผ๋ก ์ฌ์ฉํ๋ ๊ธฐ๋ฒ์ ๋งํ๋ค.
์ฆ ํน์ ํด๋์ค ์ ๋ณด๋ฅผ ๋๊ฒจ์ ์์ ์ฑ์ ๋ ธ๋ฆฌ๋ ๊ธฐ๋ฒ์ TypeToken ์ด๋ผ๊ณ ํ๋ค.
ํ์
ํ ํฐ์ ์ฌ์ฉํ๋ TypeSaftyMap
static class TypeSaftyMap {
Map <Class<?>, Object> map = new HashMap<>();
<T> void put(Class<T> clazz, T value) {
map.put(clazz, value);
}
<T> get(Class<T> clazz) {
return clazz.cast(map.get(clazz));
}
}
์ด ์ฝ๋๋ ๊ฐ ๋ฉ์๋ T
๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ํ๋ Class ๋ฅผ ์๋ณํ๋ ์ ๋ค๋ฆญ์ด๋ฉฐ ๋์์ clazz ๋ฅผ ํ ํฐ์ผ๋ก ์ฌ์ฉํ๊ณ ์๋ค.
์ด๋ฅผ ํ์ํ๋ ์ฝ๋๋ ๋ค์๊ณผ ๊ฐ๋ค.
TypeSaftyMap map = new TypeSaftyMap();
map.put(String.class, "String");
map.put(Integer.class, 3);
map.put(List.class, Arrays.asList(1, 2, 3));
System.out.println(String.class);
System.out.println(Integer.class);
System.out.println(List.class);
Type Erasure
์ ๋ค๋ฆญ์ ํ์ ์๊ฑฐ์ (Type Erasure) ์ ์ํด ์์ ์ ํ์ ์์ ์ ๋ณด๋ฅผ ์ญ์ ํ๋ค.
๋๋ฌธ์ ์๋์ ๊ฐ์ด ์ค ํ์ ๋งค๊ฐ๋ณ์ ํํ์ ์ ์ธ์ ์ปดํ์ผ ๊ณผ์ ์์ ๋ค์๊ณผ ๊ฐ์ด ๋ณ๊ฒฝํ๋ค.
Before
List<String> list = new ArrayList<>();
After
ArrayList list = new ArrayList();
ํ์ ํ๋ผ๋ฉํฐ๋ ๋ฌผ๋ก ์ด๊ณ ์ํผํด๋์ค์ ํ์ ํ๋ผ๋ฉํฐ ๋ฐ ํด๋น ํด๋์ค์ ์ ์๋ ๋ชจ๋ ํ์ ํ๋ผ๋ฉํฐ๊ฐ ์ง์์ง๋ค.
์ ๋ค๋ฆญ์์ ์ฌ์ฉํ๋ Type Variable ์ ์ ๊ฑฐํ๊ณ ๊ธฐ๋ฐํ์
(๋ชจ๋ ๊ฐ์ฒด์ ๊ธฐ๋ฐํ์
์ธ Object
) ์ ์ฝ์
ํ๋ค.
J2SE 5 ์ด์ ์ ๋ค๋ฆญ์ด ์ฌ์ฉ๋์ง ์๋ ์ฝ๋์ ํธํ์ฑ์ ๋ณด์ฅํ๊ธฐ ์ํด์ฌ ์ง์๋๋ ๋ง์ด๊ทธ๋ ์ด์ ๊ธฐ๋ฅ ์ค์ ํ๋์ด๋ค.
์ด๋ ์ ๋ค๋ฆญ ์ถํ ์ด์ ํ์ ํธํ์ฑ (Java SE 1.4 ์ดํ) ์ ์ํด์ Byte Code ๋ก ์ ํํ๋ ๊ณผ์ ์์ ์ ๋ค๋ฆญ ํ์ ์ ์ ๊ฑฐํ๋ค.
์ ์ฝ๋๋ฅผ ์์๋ก ์๋์ ๊ฐ์ด ์์ฑํด๋ณด์
map.put(List.class, Arrays.asList(1, 2, 3));
map.put(List.class, Arrays.asList("1", "2", "3"));
์ ์ฝ๋์์๋ ๋ฌธ์ ์ ์ ๋์ผํ ํด๋์ค์ ๋ค๋ฅธ ๊ฐ์ ๋ฃ์ผ๋ฉด ๋ฎ์ด ์์ฌ์ง๋ค.
๋ํ ๊ฐ์ ๋ค์ด๊ฐ๋ ๋๊ฐ์ ๊ฐ์ฒด๋ ์๋ก ๋ค๋ฅธ ์ ๋ค๋ฆญ ๊ฐ์ด๊ธฐ ๋๋ฌธ์ ๋ค๋ฅธ ํ์
์ด๋ค.
์๋ฐํ ๋งํ๋ฉด List.class
๊ฐ ์๋ List<Integer>.class
List<String>.class
์ด๋ค.
์ด ๋๊ฐ๋ฅผ ๋์ผํ๊ฒ ์๋์ ๊ฐ์ด ๋์
ํ๋ฉด ์๋ฌ์ด๋ค.
map.put(List<Integer>.class, Arrays.asList(1, 2, 3));
map.put(List<String>.class, Arrays.asList("1", "2", "3"));
์ด์ ๊ฐ์ด ํ์ ์๊ฑฐ์ ์ํด ๋ฐํ์ ์์ ์ ํ์ ์์ ์ฑ์ด ๋ณด์ฅ๋์ง ์๊ธฐ ๋๋ฌธ์ Super Type Token ์ด ์ถํํ์๋ค.
ํ์ ๊ตฌ์ฒดํ (Type Reification)
Java ์๋ ๋ค๋ฅด๊ฒ C# ์์๋ ์ ๋ค๋ฆญ ์ฌ์ฉ์ ํ์ ์๊ฑฐ๊ฐ ์๋ ํ์ ๊ตฌ์ฒดํ ๋ฐฉ์์ ํตํด ์ ๋ค๋ฆญ์ ๊ตฌํํ๋ค. ์ด๋ ์ปดํ์ผ์ Byte Code ๋ณํ์์๋ ํ์ ์ด ์๊ฑฐ๊ฐ ๋์ง ์๊ณ ์ค ํ์ ์ ๋ณด๋ฅผ Byte Code ์์ ๋ณด์กดํ๋๊ฒ์ธ๋ฐ
์ด๋ก ์ธํด C# ์ ์ ๋ค๋ฆญ ์ถํ ์ด์ ์ ํ์ํธํ์ฑ์ ํฌ๊ธฐํ๋ ๋ฐฉ์์ ์ฑํํ ๊ฒ์ด๋ค.
DANGER
Bounded Type ์ ์ฝ ์กฐ๊ฑด์ ํ์ ์๊ฑฐ์ ํฌํจ๋์ง ์๋๋ค.
Super Type Token
์ผ๋ฐ์ ์ผ๋ก ์ ๋ค๋ฆญ ํ์ ์ ์ปดํ์ผ ์์ (Byte Code ๋ก ๋ณํ) ์ Type Erasure ์ ์ํด ํ์ ์ ๋ณด๊ฐ ์๊ฑฐ๋๋๋ฐ
๋ถ๋ชจ ํด๋์ค (Super Class) ๋ฅผ ์ ๋ค๋ฆญ ํ์ ์ ํตํด ์์์ด ๋๋ฉด ์ปดํ์ผ (Byte Code ๋ก ๋ณํ) ํด๋ ์ ๋ค๋ฆญ ์ ๋ณด (์ค ํ์ ์ธ์) ๊ฐ ๋ณด์กด๋์ด ๋ฐํ์ ์์ Reflection ์ ์ด์ฉํ์ฌ ํ์ ์ ๋ณด๋ฅผ ์ป์ด์ฌ ์ ์๋ค.
// ์ ๋ค๋ฆญ ๋ถ๋ชจ ํด๋์ค
class Parent<T> {
T value;
}
// ์ ์ ์ผ๋ก ์ ์๋ ๋ถ๋ชจ๋ฅผ ์์ํ๋ ์๋ธ ํด๋์ค
class Child extends Parent<String> {
...
}
๋ฆฌํ๋ ์ ์ ์ด์ฉํ ๋ถ๋ชจ์ ์ ๋ค๋ฆญ ํ์ ๊ตฌํ๊ธฐ
Child child = new Child();
// ๋ถ๋ชจ์ ์ ๋ค๋ฆญ ํด๋์ค ์ ๋ณด๋ฅผ ์ป์
Type type = child.getClass().getGenericSuperclass();
// ํ์
์ผ๋ก๋ถํฐ ํ๋ผ๋ฉํฐ ํ์
์ ๋ค์ ์ป์
ParameterizedType paramType = (ParameterizedType) type;
// ํ๋ผ๋ฉํฐ ํ์
์ ์ธ์๋ฅผ ํ์ธํ๋ฉด ๊ทธ ์์ ์ ๋ค๋ฆญ ํ์
์ด ๋ค์ด์์
System.out.println(paramType.getActualTypeArguments()[0]); // String
์๋ฅผ ํ์ฉํ์ฌ ์ถ์ํํ TypeReference
ํด๋์ค๋ฅผ ์์ฑํ๋ค.
TypeReference Class
abstract class TypeReference<T> {
Type type;
public TypeReference() {
Type parentType = getClass.getGenericSuperclass();
this.type = ((ParameterizedType) parentType).getActualTypeArguments()[0];
}
}
์์ฑ๋ TypeReference
ํด๋์ค๋ฅผ ์ด์ฉํ์ฌ TypeSaftyMap
์ ๊ฐ์ ํ๋ค.
๊ฐ์ ๋ TypeSaftyMap
Class
class TypeSaftyMap {
Map<TypeReference<?>, Object> map = new HashMap<>();
<T> void put(TypeReference<T> tRef, T value) {
map.put(tRef, value);
}
<T> get(TypeReference<T> tRef) {
return ((Class<T>) tRef.type).cast(map.get(tRef));
}
}
์ ์ฝ๋๋ TypeReference
ํด๋์ค๊ฐ ์ธ์คํด์ค ํ์ผ๋ก get
๋ฉ์๋์ ์ฝ์
๋์ด ์ฐธ์กฐํ๊ฐ ํ ํฐ์ผ๋ก ๋์ด ๋ฒ๋ ธ๋ค.
์ด๋ฅผ ๋ณด์ํ๋ ค๋ฉด TypeReference ํด๋์ค๊ฐ ํด์๋งต์์ ์ธ์คํด์คํ์ผ๋ก ์๋ณ๋์ง ์๊ณ ํ์
์ผ๋ก ์๋ณ ๋๋๋ก hashCode
์ equals
๋ฅผ ์ฌ์ ์ ํด์ค์ผ ํ๋ค.
hashCode ์ equals ๋ฅผ ์ฌ์ ์ํ TypeReference
abstract class TypeReference<T> {
Type type;
public TypeReference() {
Type parentType = getClass().getGenericSuperclass();
if (parentType instanceof ParameterizedType) {
this.type = ((ParameterizedType) parentType).getActualTypeArguments()[0];
} else throw new RuntimeException();
}
public int hashCode() {
return type.hashCode();
}
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null || getClass().getSuperclass() != obj.getClass().getSuperclass()) return false;
TypeReference<?> that = (TypeReference<?>) obj;
return type.equals(that.type);
}
public String toString() {
return type.getTypeName();
}
}
์ ๋ค๋ฆญ ์ต๋ช ์ธ์คํด์ค ํ์ธ
TypeSaftyMap map = new TypeSaftyMap();
map.put(new TypeReference<Integer>(){}, 3);
map.put(new TypeReference<String>(){}, "ABC");
// java.lang.Integer
System.out.println(new TypeReference<Integer>(){});
// java.lang.String
System.out.println(new TypeReference<String>(){});
// 3
System.out.println(map.get(new TypeReference<Integer>(){}));
// ABC
System.out.println(map.get(new TypeReference<String>(){}));
//์๋ฌ๋ฐ์
System.out.println(map.get(new TypeReference<List<Integer>>(){}));
//์๋ฌ๋ฐ์
System.out.println(map.get(new TypeReference<List<String>>(){}));
์๋ธ ์ ๋ค๋ฆญ ์ต๋ช ํด๋์ค ๋ณ๊ฒฝ
class TypeSaftyMap {
Map<TypeReference<?>, Object> map = new HashMap<>();
<T> void put(TypeReference<T> typeRef, T value) {
map.put(typeRef, value);
}
<T> get(TypeReference<T> typeRef) {
// ์ผ๋ฐ ํด๋์ค
if (typeRef.type instanceof Class<?>) {
return ((Class<T>) typeRef).cast(map.get(typeRef));
} else {
return ((Class<T>) ((ParameterizedType) typeRef.type).getRawType()).case(map.get(typeRef));
}
}
}
์ต๋ช ํด๋์ค๋ฅผ ์ฌ์ฉํ๋ ์ด์ ๋ ์ ๋ค๋ฆญ ํ์ ํ๋ผ๋ฉํฐ๋ฅผ ์ ๋ฌํ๊ธฐ ์ํ ์ฉ๋ ์ด๋ค.
์๋ธ ์ ๋ค๋ฆญ ํด๋์ค ํ์ธ
TypeSaftyMap map = new TypeSaftyMap();
map.put(new TypeReference<List<Integer>>(){}, Array.asList(1,2,3));
map.put(new TypeReference<List<String>>(){}, Array.asList("a","b","c"));
//[1,2,3]
System.out.println(map.get(new TypeReference<List<Integer>>(){}));
//["a","b","c"]
System.out.println(map.get(new TypeReference<List<String>>(){}));