类型擦除及其解决方案 TypeToken 或TypeReference

类型擦除名词解释与存在问题

当我们使用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);

输出结果:

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值