使用 gson,出现 LinkedTreeMap cannot be cast to xxx

一:正文前面的 叨叨

最近在写一个请求封装的时候翻车了,只能怪自己太菜了,在开始之前先看一下整体的流程(不需要的地方已经简化了)。

在一个正常的请求中,有很多个实体类(JavaBean),每个接口返回的数据也是各有千秋,均不相同,但是所有的接口最外层,有一层他们公有的结构体,例如:{“code”:1000,“msg”:“成功”,“data”:""};
于是为了不写重复的代码,我们可以将最外层的解析不写在每个请求的各自实现中,而是写在封装好的方法里,这里我的写法是这样的:

1:首先定义自己的数据返回体,利用接口将数据返回到上层;

(1)定义所有接口返回的最外层 基础结
public class BaseEntity<T> {
    public String msg;
    public int code;
    public T data;
}
(2)定义符合所有请求返回数据的接口:
    public interface RequestListener<T> {
        void onSuccess(T data);
    }

2:其次完成 所有请求的封装:

     /**
     * 这是模拟后台请求
     *
     * @param listener 用于传递数据的回调
     * @param <T>      需要转换data 的对象类型
     */
    private <T> void request(RequestListener<T> listener) {
        String serverStr = "这里是后台返回的内容";
        Type type = new TypeToken<BaseEntity<T>>() {
        }.getType();
        BaseEntity<T> baseEntity = new Gson().fromJson(serverStr, type);
        T data = baseEntity.data;
        listener.onSuccess(data);
    }

3:发起一个请求(在这里我就只模拟一下简单的数据结构,请求在本文中是非重点):

(1)定义学生 Student 的JavaBean
public class Student {
    public String name;
    public int age;
    public double score;
}

(2):发起一个 获取学生的请求:
    private void getStudent() {
        request(new RequestListener<Student>() {
            @Override
            public void onSuccess(Student data) {

            }
        });
    }
(3)发起一个 获取所有学生的请求
    private void getStudents() {
        request(new RequestListener<List<Student>>() {
            @Override
            public void onSuccess(List<Student> data) {

            }
        });
    }

4:开始准备数据进行测试:

(1)获取所有学生 请求,准备后台返回的数据:

数据:

{
    "code":1000,
    "msg":"成功",
    "data":[
        {
            "age":19,
            "name":"张三",
            "score":95.5
        },
        {
            "age":25,
            "name":"李四",
            "score":66.6
        },
        {
            "age":20,
            "name":"王五",
            "score":75
        },
        {
            "age":22,
            "name":"马六",
            "score":100
        }
    ]
}

代码请求:
在这里插入图片描述
看着貌似也没啥问题对吧?

(2)获取学生 请求,准备后台返回的数据:

数据:

{
    "code":1000,
    "msg":"成功",
    "data":{
        "age":19,
        "name":"张三",
        "score":95.5
    }
}

代码请求:
直接崩溃。接下来进入今日正文
**

二:直接崩溃 从这里是本文重点

1 发现错误:

**
报错信息 java.lang.ClassCastException: com.google.gson.internal.LinkedTreeMap cannot be cast Student;
凭借多年老油条的经验,凭直觉感觉,这里翻车了,因为一时半会想不起来哪里出了问题,而且获取所有学生信息都以及成功拿到数据了啊,这里为啥会翻车呢?

好了,不买关子了,其实细心的同学发现,在上面获取 所有学生信息接口的时候,List中明明是我定义的 Student 为什么在拿到数据时时 LinkedTreeMap?
在这里插入图片描述

这样就能说明其实获取全部学生时已经出现问题了,只不过我们并没有取到里面的item;
所以到这里就应该明白,其实我们的封装出问题了。

2 错误分析:

回过头来检查 我们的封装方法,其实一眼望去没啥大问题,就那么几行代码,连个警告都没出现,很完美的代码,但是还是出bug了。
查了下百度,找资料查原因,最终找到了一段解释:

如果指定了T 类型为 Student 在自己的方法中是可以知道T 就是Student,但是如果到了封装方法中,T 并未真正转型,如果在其他真正支持泛型的语言里,如C#,上述代码是可以正确执行的。原因是JAVA实现的泛型是伪泛型。jdk1.5以后,在class文件里,method(List list)和method(List list)方法描述是一样的,只是额外新增了方法签名(Signature)和LocalVariableTypeTable保存对应方法的泛型信息。只有在赋值操作时才用到泛型信息,用法只是做了下强制转换而已。这也是通常所说的类型擦除。在整个父类方法执行的过程中,是不知道泛型信息的,而成功反序列化依赖外部调用方法的泛型信息。在不知道类型的情况下,只能解析为Object, Gson 用Map存储这类结果,就是报错内容。

3 解决错误

1:在封装方法中,不直接使用 Type ,修改封装方法
(1)RequestListener

在 RequestListener 接口中暴露 添加 Type getType();方法,在各自的调用方直接写明我这次需要的对象;

(2)封装 request 方法中

修改前:

Type type = new TypeToken<BaseEntity<T>>() { }.getType();
BaseEntity<T> baseEntity = new Gson().fromJson(serverStr, type);

修改后:

BaseEntity<T> baseEntity = new Gson().fromJson(serverStr, listener.getType());
(3)各自请求
 private void getStudent() {
        request(new RequestListener<Student>() {
            @Override
            public void onSuccess(Student data) {
            }
            @Override
            public Type getType() {
                return new TypeToken<BaseEntity<Student>>() {
                }.getType();
            }
        });

    }

    private void getStudents() {
        request(new RequestListener<List<Student>>() {
            @Override
            public void onSuccess(List<Student> data) {

            }
            @Override
            public Type getType() {
                return new TypeToken<BaseEntity<List<Student>>>() {
                }.getType();
            }
        });
    }
(4)修改后验证请求,后台数据还是原来的数据,就不做修改了。

在这里插入图片描述

在这里插入图片描述

2 更换解析框架 猝…

三:写后叨叨

作为一名优秀的 猿,就应该有勇于研究的勇气,在这里记录一下这次翻车事故现场,如嫌太菜,求轻喷…

  • 17
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 6
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值