일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- Exception
- di
- SOA
- ddd
- jvm
- OOP
- JWT
- bounded context
- Generic
- JSON
- AOP
- IOC
- Java
- bytecode
- Rest
- *
- rest api
- mockito
- MSA
- Transaction
- PSA
- 서명
- reflection
- Spring Data Redis
- junit5
- redis
- spring
- Today
- Total
개발자일기
java generic, 슈퍼 타입 토큰 본문
1. Generics
자바 5에서 추가된 기능으로서 제네릭(Generic)은 클래스 내부에서 사용할 데이터 타입을 외부에서 지정하는 기법을 의미한다
static 메소드 제네릭
public static class Utils{
public static <T> int size(T[] anArray) {
return anArray.length;
}
}
클래스 제네릭
class MyClass<T> {
T t;
private MyClass(T t){
this.t = t;
}
T getT(){
return t;
}
}
2. type eraser
자바는 컴파일시에 제네릭 타입 정보들을 제거 한다.
런타임시에는 실제로 타입정보는 존재 하지 않고 강제 캐스팅 된다.
class MyClass {
Object t;
private MyClass(Object t){
this.t = t;
}
Object getT(){
return (String) t; //이렇게 강제 캐스팅된다.
}
Type getTypeName(){ //결과는 Object타입이다.
try {
return this.getClass().getDeclaredField("t").getType();
} catch (NoSuchFieldException e) {
e.printStackTrace();
return null;
}
}
}
typeSafetyMap 예제
public class Test {
public static void main(String[] arg) {
TypeSafetyMap map = new TypeSafetyMap();
map.put(Integer.class, 1);
map.put(Integer.class, "hello");
map.put(String.class, "hello");
map.put(String.class, 2);
map.put(List<String>.class, Arrays.as("A","B","C"));
map.put(List<Integer>.class, Arrays.as(1,2,3));
//List.class 여서 덮어 씌우게 된다.
}
}
class TypeSafetyMap {
Map<Class<?>, Object> map;
{
this.map = new HashMap<>();
}
public <T> void put(Class<T> clazz, T t){
map.put(clazz, t);
}
public <T> T get(Class<T> clazz){
return clazz.cast(map.get(clazz));
}
}
3.슈퍼타입토큰
먼저 타입 토큰에 대해 알아보자.
클래스 리터럴 : String.class, Integer.class 등을 말하며, String.class의 타입은 Class<String>, Integer.class의 타입은 Class<Integer>다.
타입 토큰 : 컴파일타임 타입 정보와 런타임 타입 정보를 알아내기 위해 메서드들이 주고받는 클래스 리터럴을 타입토큰이라한다.
슈퍼 타입토큰
위의 type eraser내용에서 보듯이 List<String>.class와 같은 클래스 리터럴은 존재 하지 않는다
이런 제네릭 정보가 지워지는 문제 때문에 Super type token 기법이 생겨났다.
Super type token은 수퍼(상위)타입을 토큰으로 사용하겠다는 의미이다.
무슨말인가?
제네릭 정보가 컴파일시 런타임시 다 지워지지만 제네릭 정보를 런타임시 가져올 방법이 존재한다. 제네릭 클래스를 정의한 후에 그 제네릭 클래스를 상속받으면 런타임시에는 제네릭 정보를 가져올 수 있다.
슈퍼 타입을 토큰으로 사용한다는 의미이다. 말이 어렵지만 예제를 통해 풀어보자
List<String>.class 리터럴의 토큰 타입 Class<List<String>> 을 어떻게 구할까
바로 Class의 메소드의 public Type getGenericSuperclass() 메소드를 통해 구할수 있다.
getGenericSuperclass() 이용 하여 바로 위의 슈퍼 클래스의 타입을 반환한다.
상위타입은 제네릭의 타입토큰 정보가 존재한다.
public abstract class TypeReference<T> {
private Type type;
protected TypeReference() {
Type superClassType = getClass().getGenericSuperclass();
if (!(superClassType instanceof ParameterizedType)) { // sanity check
throw new IllegalArgumentException("TypeReference는 항상 실제 타입 파라미터 정보와 함께 생성되어야 합니다.");
}
this.type = ((ParameterizedType)superClassType).getActualTypeArguments()[0];
}
public Type getType() {
return type;
}
}
public void main(String[] args){
TypeReference<List<String>> ref = new TypeReference<List<String>>(){};//상속받는다
//바로위의 상위 타입에 대한 제네릭 타입은 존재한다.
Type type = ref.getType(); // List<String>.class의 타입정보이다.
}
슈퍼(상위)타입의 제네릭 파라미터정보인 Type을 통해 제네릭 파라미터 클래스 정보를 가져온다.
=> 최종적으로 T타입에 대한 정보를 가져올수있다.
@SuppressWarnings("unchecked")
private Class<T> getGenericTypeClass() {
try {
String className = ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0].getTypeName();
Class<?> clazz = Class.forName(className);
return (Class<T>) clazz;
} catch (Exception e) {
throw new IllegalStateException("Class is not parametrized with generic type!!! Please use extends <> ");
}
}
Spring 에서는 ParameterizedTypeReference 클래스를 제공해 json serialize/deserialize 에서 사용한다.
'개발' 카테고리의 다른 글
DB 트랜잭션과 lock (0) | 2020.05.10 |
---|---|
도메인 주도 개발 (0) | 2020.04.30 |
동기/비동기 블로킹/논블로킹 차이 (0) | 2020.04.15 |
JWT(JSON WEB TOKEN) 개념 (0) | 2020.04.12 |
암호화 방식과 HTTPS (0) | 2020.04.10 |