类型擦除名词解释与存在问题
当我们使用Java的泛型时,编译器会在编译时执行类型擦除。类型擦除是指编译器会将泛型类型转换为其原始类型,以便在运行时使用。例如,List<String>
将被转换为List<Object>
,在运行时不再有类型参数。这种类型擦除的机制导致在反序列化时,我们无法获取实际的泛型类型信息,而只能得到原始类型,这就是类型擦除所带来的问题。
听起来很懵,没关系,看一下例子你就知道了。
案例
1. 泛型类 ApiResponse
package org.example.springboot.job.domain;
import lombok.Data;
public class ApiResponse<T> {
private int code;
private String message;
private T data;
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
}
2. User类
package org.example.springboot.job.domain;
public class User {
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
3. 场景举例
访问其它的系统,返回了这样的一个结果:
{
"code": 200,
"message": "success",
"data": {
"name": "John",
"age": 25
}
}
我们想用ApiResponse<User>
去接收这个返回值
public static void main(String[] args) {
String json = "{\n" +
" \"code\": 200,\n" +
" \"message\": \"success\",\n" +
" \"data\": {\n" +
" \"name\": \"John\",\n" +
" \"age\": 25\n" +
" }\n" +
"}";
Gson gson = new GsonBuilder().create();
ApiResponse<User> response = gson.fromJson(json, ApiResponse.class);
}
执行结果,没有任何的报错。有小伙伴可能就要说了,什么类型擦除啊,我照接收不误。
debug看看
你家的User对象呢?怎么成了别人家的LinkedTreeMap了?
因为运行时泛型被擦除了,所以得到的其实是Object
,那么后面的Gson就不知道要转成User
类型了,这时Gson会默认转成LinkedTreeMap类型。
有小伙伴就要说了,我管它什么类型,能拿出来就行了。好,我们拿一下看看
System.out.println(response.getData().getName());
运行一下
怎么类型转换错误啦,拿不出来哦
解决类型擦除带来的问题
为了解决这个问题,我们可以使用一些框架提供的解决方案,例如Gson库中的TypeToken和fastjson库中的TypeReference。这两个类的作用是通过构造一个包含泛型类型信息的对象来绕过类型擦除的限制,从而使我们能够在反序列化时获取到实际的泛型类型信息。
1. 以Gson库为例,使用TypeToken解决类型擦除的问题
public static void main(String[] args) {
String json = "{\n" +
" \"code\": 200,\n" +
" \"message\": \"success\",\n" +
" \"data\": {\n" +
" \"name\": \"John\",\n" +
" \"age\": 25\n" +
" }\n" +
"}";
Gson gson = new GsonBuilder().create();
TypeToken<ApiResponse<User>> typeToken = new TypeToken<ApiResponse<User>>() {};
ApiResponse<User> response = gson.fromJson(json, typeToken.getType());
System.out.println(response.getData().getClass().getTypeName());
System.out.println(response.getData().getName());
System.out.println(response);
}
运行结果:
2. 用fastjson的TypeReference解决
TypeReference<ApiResponse<User>> typeRef = new TypeReference<ApiResponse<User>>() {};
ApiResponse<User> response = JSON.parseObject(json, typeRef);
System.out.println(response.getData().getClass().getTypeName());
System.out.println(response.getData().getName());
System.out.println(response);
输出结果: