===== 2-1 Super Type Token ===== java-spring 토비 강의

===== 2-1 Super Type Token =====
Generics 
// gafter.blogspot.kr/2006/12/super-type-tokens.html


<code>
public class TypeToken {
static Object create() {
return new Object();
}

public static void main(String[] args) {
Object o = create();
System.out.println(o.getClass());
}
}


public class TypeToken {
static <T> T create(Class<T> clazz) throws Exception {
//캐시스팅 없이 되는 안전한 코드가 된다 . (type eraser)
return clazz.newInstance();
}

public static void main(String[] args) throws Exception {
//Integer o = create(Integer.class);
//에러 남 Integet 는 디폴트 생성자가 없기 때문이다. (System.out.println(new Integer()) 가 안됨 )
Object o = create(String.class);
System.out.println(o.getClass());
}
}


public class TypeToken {
static class Generic<T> {
T value;
void set(T t) {}
T get() { return null; }
}

public static void main(String[] args) throws Exception {
Generic<String> s = new Generic<String>();
s.value = "String";
// type eraser 타입 파라미터를 파라미터라이즈드 타입 Generic 런타임시에 삭제되어 오브젝트 타입이 된다. 그래서 실제 코드 안에 타입 캐스팅을 넣어줌. 
//ex) s.value = (String)"String";

Generic<Integr> i = new Generic<Integer>();
i.value = 1;
i.set(10);
}
}
</code>

타입 안정성 있는 맵
<code>
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 (T) map.get(clazz)) 도 되지만 타입캐스팅을 하기 때문에 좋은 코득 ㅏ아니다 위험! 
return clazz.cast(map.get(clazz));
}
}

//이렇게 타입정보를 값으로 넘기는 것 : type token

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

}
}
</code>

Neal Gafter 님의 블로그 좋음. 

자바가 처음에는 제네릭이 없었다. 
List<String> 
컴파일 되어 바이트 코드로 만든느 방법 .
1. eraser : 자기 정보를 다 날리고 Object로 바꿔버림. 
장점 : 자바 1.4 때까지 있던 라이브러리와 코드를 그대로 사용 가능 .(호환)
(JAVA) JAVA 5 이전의 개발이 많았다. 
2. refication (refiable type으로 만듬 ) : 구체화 하는 것. 
(c#) : 이 때 만들어진지 얼마 안되어 호환 무시! 


<code>
public class SuperTypeToken {
static class Sup<T> {
T value;
}

public static void main(String[] args) throws Exception {
Sup<String> s = new Sup<>();
System.out.println(s.getClass().getDeclaredFiled("value").getType());
//eraser방식이기 때문에 다 사라져서 String이 아닌 Object가 된다. 
}
}
</code>


<code>
public class SuperTypeToken {
static class Sup<T> {
T value;
}

static class Sub extends Sup<String> {

}

public static void main(String[] args) throws Exception {
Sup s = new Sup();
//클래스 이름만 쓴건 타입 .Object
Type t = b.getClass.getGenericSuperClass();
// Sup<String> 클래스가 들어있는 타입. 
ParameterizedType ptype = (ParameterizedType)t;
System.out.println(ptype.getActualTypeArgument()[0]);
}
}
</code>
  - 클래스의 인스턴스를 만들면서 넣어준 클래스는 호환성 문제때문에 삭제시킨다. 
  - 새로운 타입을 정의하면서 수퍼 클래스를 generic class(타입 파라미터를 가진 ) 으로 지정하면 이 정보는 reflection을 통하여 지정되도록 바이트코드에 남아있다. 
  - 이 때 Sub<String>뿐만 아니라 Sup<List<String>>같이 중첩된 타입 정보도 다 가지고 온다. 


로컬 클래스 : 클래스의 스코프를 로컬로 지정  메소드 안에서만 한번 사용한다. (이름 생략해도 되 생략하면 : annonymous class)
Nested static class == INNER class? 

<code>
public class SuperTypeToken {
static class Sup<T> {
T value;
}


public static void main(String[] args) throws Exception {
//Local Class
class Sub extends Sup<List<String>> { }
//annomymous class
Sup b = new Sub<List<String>>(){};
//inline
Type t = (new Sub<List<String>>() {}).getClass.getGenericSuperClass();


Sup s = new Sup();
Type t = b.getClass.getGenericSuperClass();
ParameterizedType ptype = (ParameterizedType)t;
System.out.println(ptype.getActualTypeArgument()[0]);
}
}
</code>

Spring 
ParameterizedTypeReference가 있다. 
<code>
public class SpringTypeReference {
public static void main (String[] args) {
//spring 3.2 이상 .
//왜 {}를 붙여야 하는가. -> 익명 클래스의 인스턴스를 만들어서 익명 클래스가 상속하는 super class의 generic type parameter정보를 전달하기 위한 이유입니다. 
ParameterizedTypeReference type = new ParameterizedTypeReference<List<Map<Set<Integer>,String>>>() {};
System.out.println(typeRef.getType());
}
}
</code>
//사용하는 일 
//reflection에 대한 감은 필요. 
<code>

@SpringBootApplication
public class TobytvApplication {
public static void main(String[] args) {
SpringApplication.run(TobytvApplication.class,args);
}

@RestController
public static class MyController { 
@RequestMapping("/")
public List<User> users(){
//json의 루트가 array로 된다. 
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() {

}

}
}

// 이걸 resttemplate으로 끌어올 때 
public class SpringTypeReference {
public static void main(String[] args){
RestTemplate rt = new Resttemplate();
//List.class 가 type token이다. generic이 없을때 용이하다. 
//Linked hashMap으로 바뀐다.  List<User>라는걸 몰라서 가장 가까운 녀석으로 출력한다.  map.
//그래서 아래 안되! 
List<User> users = rt.getForObject("http://localhost:8080", List.class);

//exchange는 header와 http status, body를 다 가지고 있는 entity를 가지고 있어서 get body 이용. 
List<User> users = rt.exchange("http://localhost:8080", HttpMethod.get, null, new ParameterizedTypeReference<List<User>>() {}).getBody();
System.out.println(users.get(0).getName());

users.forEach(System.out::pringln);
}
}
</code>

new ParameterizedTypeReference<List<User>>() {}
매번 만들어야 하니까 한번 캐시 하거나 변수에 저장하는 방법도 좋을것같다. 


<code>
@SpringBootApplication 
public class TobyApplication{
static class GenericService<T> {
T t;

@PostConstruct
void init(){
Class tType;
t = ctx.getBean(tType);
}
}

@Component 
static class MyService extends GenericService<MyBean1> {
}

@Component 
static class MyService2 extends GenericService<MyBean2> {
}
}

</code>
spring 4.2부터 가능 .
<code>
@SpringBootApplication 
public class TobyApplication{

static class GenericService<T> {
@Autowired
T t;

@Autowired
R r;

@Autowired
S s;

}

@Component 
static class MyService extends GenericService<MyBean1> {
}

@Component 
static class MyService2 extends GenericService<MyBean2> {
}
}

</code>

덧글

댓글 입력 영역