深入理解 `ParameterizedTypeReference`:解决 Java 泛型擦除问题

在 Java 中,由于类型擦除的存在,我们在使用 RestTemplate 获取带有泛型的 HTTP 响应时,可能会遇到 泛型信息丢失 的问题。而 ParameterizedTypeReference<T> 正是用来解决这个问题的。

本文将深入解析 ParameterizedTypeReference 的作用、原理,并结合 RestTemplate 举例说明如何正确解析泛型数据


1. 为什么 Java 需要 ParameterizedTypeReference

在 Java 中,泛型的实现采用类型擦除(Type Erasure),即:

  • 编译时 泛型信息是完整的,例如 List<String>List<Integer>
  • 运行时 泛型信息被擦除,所有泛型类型都会变成 List,无法区分 List<String>List<Integer>

示例:Java 运行时擦除泛型

List<String> list1 = new ArrayList<>();
List<Integer> list2 = new ArrayList<>();

System.out.println(list1.getClass() == list2.getClass()); // true

输出:

true

可以看出,List<String>List<Integer> 在运行时完全相同。


2. RestTemplate 解析泛型数据时的问题

假设我们有一个 API /api/users,返回 JSON 数据如下:

{
    "code": "SUCCESS",
    "message": "请求成功",
    "data": [
        {
            "id": 1,
            "name": "Alice"
        },
        {
            "id": 2,
            "name": "Bob"
        }
    ]
}

对应的 Java 数据模型

import lombok.Data;
import java.util.List;

@Data
class User {
    private Integer id;
    private String name;
}

@Data
class BaseResponse<T> {
    private String code;
    private String message;
    private T data;
}

尝试使用 RestTemplate 获取泛型数据

ResponseEntity<BaseResponse<List<User>>> response = restTemplate.exchange(
    url,
    HttpMethod.GET,
    entity,
    BaseResponse<List<User>>.class  // ❌ 这里会导致类型擦除
);

问题: BaseResponse<List<User>>.class 在运行时 会被擦除成 BaseResponse<Object>,导致 JSON 反序列化失败!

解决方案? ✅ 使用 ParameterizedTypeReference


3. 使用 ParameterizedTypeReference 解析泛型数据

ParameterizedTypeReference<T> 的作用是 保留泛型信息,从而让 RestTemplate 正确解析带泛型的 JSON 数据。

✅ 正确写法

import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.*;
import org.springframework.web.client.RestTemplate;
import java.util.List;

public class RestTemplateExample {
    private static final RestTemplate restTemplate = new RestTemplate();

    public static void main(String[] args) {
        String url = "https://example.com/api/users";

        // 创建请求头
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);

        // 创建请求实体
        HttpEntity<String> entity = new HttpEntity<>(null, headers);

        // 使用 ParameterizedTypeReference 获取泛型数据
        ResponseEntity<BaseResponse<List<User>>> response = restTemplate.exchange(
                url,
                HttpMethod.GET,
                entity,
                new ParameterizedTypeReference<BaseResponse<List<User>>>() {} // ✅ 保留泛型信息
        );

        // 解析返回数据
        if (response.getStatusCode() == HttpStatus.OK) {
            BaseResponse<List<User>> body = response.getBody();
            if (body != null && "SUCCESS".equals(body.getCode())) {
                List<User> users = body.getData();
                users.forEach(user -> System.out.println(user.getName()));
            }
        }
    }
}

ParameterizedTypeReference<T> 解决了什么?

  • 保留了 BaseResponse<List<User>> 的泛型信息,避免 Java 类型擦除
  • RestTemplate 解析 JSON 时,知道 data 字段实际是 List<User>,从而正确反序列化。

4. ParameterizedTypeReference 的底层原理

我们来看 ParameterizedTypeReference核心源码

public abstract class ParameterizedTypeReference<T> {
    private final Type type;

    protected ParameterizedTypeReference() {
        this.type = ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
    }

    public Type getType() {
        return this.type;
    }
}

原理解析

  1. ParameterizedTypeReference<T>抽象类,不能直接实例化。
  2. new ParameterizedTypeReference<BaseResponse<List<User>>>() {} 实际上是 匿名内部类,继承 ParameterizedTypeReference<T>
  3. 构造方法 中:
    • 通过 getClass().getGenericSuperclass() 获取 ParameterizedType
    • getActualTypeArguments()[0] 解析出 BaseResponse<List<User>>,并存储到 type 变量中。
  4. 这样,type 变量在运行时 不会被擦除,Spring 通过 type 解析泛型 JSON 数据。

5. 适用场景

适用于解析带泛型的 JSON 响应

  • List<T>Map<String, T>Set<T>复杂数据结构
  • 适用于 Spring WebClient 以及 消息队列(Kafka)等泛型解析

示例:解析 Map<String, List<User>>

ResponseEntity<BaseResponse<Map<String, List<User>>>> response = restTemplate.exchange(
    url,
    HttpMethod.GET,
    entity,
    new ParameterizedTypeReference<BaseResponse<Map<String, List<User>>>>() {}
);

ParameterizedTypeReference 还能解析更复杂的泛型结构,如 Map<String, List<User>>,Spring 能够正确识别 data 的数据结构!


6. 总结

  • Java 运行时会擦除泛型,导致 RestTemplate 无法正确解析泛型 JSON。
  • ParameterizedTypeReference<T> 通过匿名内部类的方式 保留泛型信息,解决类型擦除问题。
  • 适用于 RestTemplateWebClientKafka 消费者Redis 泛型缓存 等场景

建议:在 RestTemplate 解析泛型数据时,务必使用 ParameterizedTypeReference,否则 JSON 解析可能失败!


希望这篇博客能帮助你理解 ParameterizedTypeReference 的作用和应用场景!🎯

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值