qidao123.com技术社区-IT企服评测·应用市场
标题:
深入理解 `ParameterizedTypeReference`:解决 Java 泛型擦除问题
[打印本页]
作者:
十念
时间:
2025-3-14 01:21
标题:
深入理解 `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;
}
}
复制代码
原理解析
ParameterizedTypeReference<T> 是
抽象类
,不能直接实例化。
new ParameterizedTypeReference<BaseResponse<List<User>>>() {} 现实上是
匿名内部类
,继续 ParameterizedTypeReference<T>。
在
构造方法
中:
通过 getClass().getGenericSuperclass() 获取 ParameterizedType。
getActualTypeArguments()[0] 解析出 BaseResponse<List<User>>,并存储到 type 变量中。
这样,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> 通过匿名内部类的方式
保留泛型信息
,解决类型擦除问题。
实用于 RestTemplate、WebClient、Kafka 消耗者、Redis 泛型缓存 等场景
。
发起:在 RestTemplate 解析泛型数据时,务必使用 ParameterizedTypeReference,否则 JSON 解析大概失败!
盼望这篇博客能帮助你理解 ParameterizedTypeReference 的作用和应用场景!
欢迎光临 qidao123.com技术社区-IT企服评测·应用市场 (https://dis.qidao123.com/)
Powered by Discuz! X3.4