이펙티브 자바/5장 제네릭

ParameterizedTypeReference와 실체화 불가 타입

말랑공룡 2024. 5. 20. 00:33

 

ParameterizedTypeReference는 스프링 프레임워크에서 제공하는 클래스로, 제네릭 타입을 갖는 객체의 타입 정보를 보존하기 위한 목적으로 사용된다. 주로 제네릭 타입을 갖는 클래스나 메서드를 호출하고 그 결과를 가져올 때 사용된다.

일반적으로 자바의 제네릭은 런타임에 소거되기 때문에, 제네릭 타입에 대한 정보를 동적으로 추출하기 어렵다.

하지만 ParameterizedTypeReference를 사용하면 런타임에 제네릭 타입 정보를 유지할 수 있다.

 

사용 예시

ParameterizedTypeReference를 사용하는 예시를 살펴보겠다.

스프링의 RestTemplate을 사용하여 HTTP 요청을 보내고, 응답을 받을 때 제네릭 타입을 유지하고 싶은 경우에 사용할 수 있다.

 

import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.ResponseEntity;
import org.springframework.web.client.RestTemplate;

public class ParameterizedTypeReferenceExample {
    public static void main(String[] args) {
        RestTemplate restTemplate = new RestTemplate();

        ResponseEntity<String> response = restTemplate.getForEntity("https://api.example.com/data", String.class);
        String body = response.getBody();
        System.out.println("Response body: " + body);

        // 제네릭 타입을 유지하기 위해 ParameterizedTypeReference 사용
        ResponseEntity<List<MyObject>> listResponse = restTemplate.exchange(
            "https://api.example.com/data",
            HttpMethod.GET,
            null,
            new ParameterizedTypeReference<List<MyObject>>() {}
        );
        List<MyObject> dataList = listResponse.getBody();
        System.out.println("Response body with ParameterizedTypeReference: " + dataList);
    }
}

 

위의 예시에서는 RestTemplate을 사용하여 GET 요청을 보내고 응답을 받는다. 첫 번째 요청은 단순한 문자열을 받아온다.

하지만 두 번째 요청은 List<MyObject>와 같은 제네릭 타입을 받아온다. 이 때 ParameterizedTypeReference를 사용하여 제네릭 타입 정보를 유지한다.

 

주의사항

ParameterizedTypeReference를 사용할 때 주의해야 할 점은 익명 클래스를 생성한다는 점이다. 이는 익명 클래스를 생성하는 것이므로 해당 클래스의 인스턴스는 하나만 사용할 수 있다. 따라서 동일한 제네릭 타입에 대해 여러 번 사용하려면 매번 새로운 ParameterizedTypeReference 인스턴스를 생성해야 한다.

 

ParameterizedTypeReference를 사용하지 않는다면?

 

두 번째 요청에서 ParameterizedTypeReference를 사용하지 않으면 컴파일 오류가 발생한다.

이는 제네릭 타입의 실제 파라미터화된 타입 정보를 유지하지 못하기 때문이다.

제네릭 타입은 컴파일 시에만 유효하며 런타임에는 소거된다.

만약 ParameterizedTypeReference를 사용하지 않고 다음과 같이 코드를 작성한다면 컴파일 오류가 발생한다.

 

ResponseEntity<List<MyObject>> listResponse = restTemplate.exchange(
    "https://api.example.com/data",
    HttpMethod.GET,
    null,
    List.class  // 컴파일 오류!
);

 

List<E>는 실체화 불가 타입이라는 말의 뜻

 

자바에서 제네릭 타입의 인스턴스화(instantiation)에 관한 것이다.

이것은 제네릭 타입이 런타임 시에 실제 타입 정보를 유지하지 않는다는 것을 의미한다.

제네릭은 컴파일 시에만 유효하고 런타임에는 소거(erasure)된다. 따라서 컴파일러는 제네릭 타입을 사용하여 코드를 검사하지만, 런타임에는 제네릭 타입의 실제 타입 정보를 제거하여 모든 제네릭 타입을 원시 타입(raw type)으로 변환한다. 이는 호환성 및 역호환성을 유지하기 위한 것이다.

그 결과로 제네릭 타입에 대한 실체화된 타입 정보는 런타임에 사용할 수 없다. 따라서 List<String>과 같은 제네릭 타입은 런타임에 List로만 인식된다. 이것이 "List<String>이 실체화 불가 타입"이라는 말의 의미다.

실체화 불가 타입은 리플렉션(reflection)을 사용하여 제네릭 타입의 실제 타입 정보를 동적으로 추출하는 것이 불가능하다는 것을 의미한다. 따라서 런타임에서는 제네릭 타입의 파라미터화된 타입 정보를 사용하는 것은 어려워진다. 하지만 이것이 컴파일 시에는 제네릭 타입의 안전성을 보장하는 이유 중 하나이다.