DevBoi

[Java] Generic ! 본문

Language/[Java]

[Java] Generic !

HiSmith 2023. 7. 15. 02:12
반응형

제네릭에 대해서 좀 더 심화된 부분을 공부해보자

ObjectMapper에 대해서 공부를 하다가. 생긴 궁금증은 꼬리의 꼬리를 물었고,

내가 모르는 부분까지 봉착했다.

 

결과적으로 ObjectMapper를 사용할때  제네릭 관련 변수로 뺄때 왜 TypeReference를 선언해야하는지 이해가 안됬다.

정확히는 이유를 정확하게 몰랐다.

 

제네릭의 type erasure에 대해서 먼저 알아야 한다.

타입 소거라고도 한다. 런타임에 타입에 대한 정보를 버리는 것이다.

 

bounded type -> bound type

 

unbouned type -> Object

 

1) Unbounded Type

public class SmithTest {
 
    public static <T> T unboundedType(T t) {
        return t;
    }
}
public static void main(String[] args) throws NoSuchMethodException {
        final Class<SmithTest> helperClass = SmithTest.class;
 
        final Class<?> returnType = helperClass.getMethod("unboundedType", Object.class)
            .getReturnType();
        
        System.out.println("returnType.getName() = " + returnType.getName());
    }

 

2) Bounded Type

public class SmithTest {
 
    public static <T extends Smith> T boundedType(T t) {
        return t;
    }
}
  public static void main(String[] args) throws NoSuchMethodException {
        final Class<SmithTest> helperClass = SmithTest.class;
 
        final Class<?> returnType = helperClass.getMethod("boundedType", Smith.class)
            .getReturnType();
 
        System.out.println("returnType.getName() = " + returnType.getName());
    }

 

콘솔로그로 찍어보면 Smith 가 노출된다.

이는 컴파일 시점에는 사용하지만, 런타임 시점에는 버리기 떄문에, Smith로 출력이 됨을 확인 할 수 있다.

 

그러면 ObjectMapper에서 제네릭을 사용할때 런타임때 버리면 어떻게 정보를 가지고 동적으로 치환할 수 있을까?

그래서, TypeReference를 사용하는 것이다.

 

우선 대표적으로 사용하는 ObjectMapper에서 어떻게 사용하고 있는지를 확인해보자.

 

해당 TypeReference로 자바 타입에 대한 리턴값을 주고, _fromAny 메서드 2번째 파라미터로 리턴한다.

 

해당 자바 타입은 어떤 인스턴스의 종류나에 따라서, 다른 타입을 리턴한다.

해당 방식으로 JavaType이라는 인스턴스를 런타임에, 리턴해서 가지고있고, 사용할수 있게한다.

 

자. 그러면 아래와 같은 상황처럼 사용하는 것도 생각해보자

유틸성 변환 모듈이다.

 

@RequiredArgsConstructor
public class NationInterfaceImpl implements NationInterface{

  private final ObjectMapper mapper;
  @Override
  public <R> List<R> getNation(String response, Class<R> clazz) throws JsonProcessingException {

    return mapper.readValue(response,mapper.getTypeFactory().constructCollectionType(List.class,clazz));
  }
}

 

 

자 그러면 한 가지 더 

package com.practice.demo.anno;

import java.util.List;

public class Nation<T> {
  List<T> data;
  public Nation(List<T> data){
    this.data = data;
  }
}
package com.practice.demo.anno;

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
public class Korean<T> {
  Nation<T> nation;
  private String name;
}
  Korean k1 = new Korean();
      List<String> list1 = new ArrayList<>();
      list1.add("gd");
      list1.add("gd2");
      k1.setNation(new Nation(list1));
  Korean k2 = new Korean();
      List<Long> list2 = new ArrayList<>();
      list2.add(2L);
      list2.add(3L);
      k2.setNation(new Nation(list2));

대충이러면 감이 올 것이다.

Korean내 Nation의 데이터는 어떠한 형태로도 사용이 가능해서 간편해진다.

 

이게 제네릭의 간편함과 효율성이다.

반응형

'Language > [Java]' 카테고리의 다른 글

[Java] Record 패턴  (0) 2024.04.28
[Java] mapMulti 사용  (0) 2023.07.07
[Java] static block 및 instance block  (0) 2023.06.26
[Java] 제네릭을 조금 잘 써보면 어떨까  (0) 2023.06.18
[Java] Mapstruct Spring 적용하기  (0) 2023.06.14