ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [java] 슈퍼타입토큰(super type token)
    java 2022. 1. 1. 16:25

    모든 글은 토비의 봄 채널을 보며 작성하였습니다.

    https://www.youtube.com/watch?v=01sdXvZSjcI&t=1973s&ab_channel=TobyLee

    배경

    RestTemplate를 사용하다보면 파라미터바인딩을 위한 타입토큰을 넘겨주는 부분에서 생소한 ParameterizedTypeReference를 사용한 걸 볼 수 있다. 클래스 이름과 생김새를 보면 대충 아 바인딩 되길 원하는 타입을 저 안에 넣고 요청을 날리면 되겠구나 싶다. 근데 이상한 건 생성자로 성성했는데 왜 마지막에 {}가 붙냐는 것이다. 이번 기회에 파헤쳐보자.

     

    타입토큰(Type Token)

    RestTemplate을 통해 요청을 보낼 때 그 반환 타입을 타입토큰 형식으로 넘겨주는 걸 볼 수 있다. RestTemplate은 rest api를 이용해 다른 서버에게 요청을 하고 그 내용은 HTTP의 body로 넘어오게 된다(대부분 JSON). 이때 JSON으로 넘어온 값의 key, value를 map에 다시 보관하게 되고 그 map의 내용을 어떤 객체에 바인딩할지 정보가 필요하다. 당연히 RestTemplate에서 메서드롤 호출하는 지점의 반환값으로 런타임에 추측하는 건 말이 안된다. 그러기에 어떤 객체를 바인딩해야할지 정보를 담은 타입토큰을 파라미터로 넘겨주는 것이다.

     

     

    다만, 반환받길 원하는 타입이 제네릭을 포함하고 있다면 문제가 된다. 자바의 언어적 이유로(나중에 여유되면 조사) 타입토큰은 제네릭 정보를 포함해서 넘길 수 없다. 말했듯이 메서드 호출 시점에 forObject에 대한 제네릭 타입을 코드에 적어줘도 RestTemplate은 객체 바인딩 시점에 SpecialDto를 바인딩해야함을 알 수 없다. 그래서 실제 forObject의 내부를 디버깅해보면 제네릭으로 SpecialDto가 들어가야할 지점에 LinkedHashMap이 들어가게 된다. 차라리 이게 익셉션을 날리는 구조였으면 나았을 것 같은데 이상하게 실행은 잘되고, 제네릭을 꺼내 쓸 때가 되어야 ClassCastException이 나게 된다.

     

    슈퍼타입토큰(Super Type Token)

    이 문제를 해결할 방법은 슈퍼타입토큰이다. 위처럼 인스턴스 생성 시점에 제네릭 정보를 넘기는 방식을 살펴보면, 컴파일러가 컴파일 시점에 모든 제네릭 타입을 로타입으로 바꾸고 강제타입캐스팅을 적용한 방식이다. 그래서 타입토큰은 제네릭을 구분하여 보낼 방법이 없는 것이다. 그렇다면 클래스를 제네릭 정보를 포함한 클래스를 상속한 클래스는 그 정보를 갖고 있을까? 갖고 있다. 상속한 클래스가 제네릭정보를 갖고 있다면 그 정보는 런타임에도 접근할 수 있는 정보가 된다. 이제 이 개념을 활용하면 리플렉션을 통해 그 타입을 가져올 수 있다.

     

    이 개념을 갖고 위에서 보이는 ParameterizedTypeReference의 생성코드를 보면 상속한 클래스의 타입을 리플렉션해서 필드에 저장하는 걸 확인할 수있다. 단, 이 방식이 유효하려면 인스턴스 ParameterizedTypeReference의 인스턴스를 생성해 리플렉션 하는 게 아닌 ParameterizedTypeReference클래스를 상속한 익명클래스를 만들어서 그 익명클래스를 리플렉션하고 그 안에 있는 제네릭 정보를 빼와야 함을 알 수 있다. 

    AnonymousClass extends ParameterizedTypeReference<GenericDto<SpecialDto> {} ...

    즉, 위 코드블럭에 나온 AnonymousClass를 리플랙션해야 정확하게 GenericDto속 SpecialDto까지 알아낼 수 있는 것이다. 

     

    마무리

    이제 RestTemplate에서 왜 ParameterizedTypeReference을 넘기는지, 또 왜 생성시점에 {}를 넣어주는지(익명클래스 생성을 위함) 정확하게 알겠다 ^^

    'java' 카테고리의 다른 글

    [mac] java 터미널 버전 변경하기  (0) 2022.03.27
    Future 클래스 정리  (0) 2021.06.03
    Generics와 wildcard  (0) 2021.06.01
    Stream 정리 및 예제  (0) 2021.05.29
    Optional 정리  (0) 2021.05.28

    댓글

Designed by Tistory.