kotlin1.8.10问题导致gson报错TypeToken type argument must not contain a type variable

书接上回,https://blog.csdn.net/jzlhll123/article/details/139302991。

之前我发现gson报错后:

gson在2.11.0给我的kotlin项目代码报错了。

IllegalArgumentException: TypeToken type argument must not contain a type variable

上次解释原因是因为,gson内部有了报错,根本原因就是二级嵌套泛型获取的解析方式问题。而且写下了解决办法和参考代码。 是错误的。

我后来又通过在demo工程中尝试,怎么都无法复现,困扰了我好几天。我又去研究了字节码和反编译。

最后发现,

kotlin 复制代码
public class ApiBean {
    public String type;
    public String name;
    public String url;
}

public class CmdBean<T>  { //T就可以传入ApiBean做为二层嵌套泛型
    public String cmdId;
    public long ts;
    public String pro;
    public T apiBean;
}

//二层嵌套解析 //暂时有问题。后面会讲到他没问题
inline fun <reified T> parse(jsonStr: String?): CmdBean<T>? {
    return jsonStr?.fromJson<CmdBean<T>>()
}

//二层嵌套解析 //暂时有问题。后面会讲到他没问题
inline fun <reified T> parseList(jsonStr: String?): List<T>? {
    return jsonStr?.fromJson<List<T>>()
}

//通用函数 //暂时有问题。后面会讲到他没问题
inline fun <reified T> String.fromJson(): T {
    return Globals.gson.fromJson(this, object : TypeToken<T>() {}.type)
}

class Action {
    fun test() {
        val json = """
{"apiBean":{"name":"nameX","type":"typeX","url":"htpps://api.....com/.../..xxx"},"cmdId":"cmdId010102394","pro":"aaaa","ts":1029301291023}
        """.trimIndent()
        val cmd = parse<ApiBean>(json)
        println("cmd $cmd")
    }

    fun testList() {
        val json = """
[{"name":"icon1","type":"type1","url":"https://.......png"},{"name":"icon2","type":"type2","url":"https://..xx.....png"}]
        """.trimIndent()
        val cmd = parseList<ApiBean>(json)
        println("list $cmd")
    }
}

上述代码是很简单的,主要就是有一个嵌套的解析:
parse<ApiBean>(json), parse函数内部,就是jsonStr?.fromJson<CmdBean<T>>()这样嵌套去解析。然后这个函数又经过一个普通的封装函数fromJson来解析。

在本地kotlin版本

复制代码
 id 'org.jetbrains.kotlin.android' version '1.8.10' apply false
 //也可能写作:
 kotlin = "1.8.10"
 jetbrains-kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }

在1.8.10之下:

编译查看class:
由此,我们就能知道为什么会报错了。原因就是字节码写了new出来的匿名内部类是个带T的,所以因为JVM上泛型擦除出现了T,gson的TypeToken就无法解析到真实的类,因此出现了问题。当我反复对比找各种原因,最后终于怀疑到了版本上。修改到kotlin1.8.20,kotlin1.9.0, kotlin 1.9.24均发现ok 了。对比的图如下:

很明显,对于这种嵌套型的inline+reified从kotlin1.8.20开始,生成的内部类就不会再有问题。

并且,gson官方文档提到的解决方案:

复制代码
Use TypeToken.getParameterized(...), for example TypeToken.getParameterized(List.class, elementType) where elementType is a type you have to provide separately.
For Kotlin users: Use reified type parameters, that means change <T> to <reified T>, if possible. If you have a chain of functions with type parameters you will probably have to make all of them reified.

其实对于kotlin项目而言,

kotlin 复制代码
inline fun <reified T> String.fromJson(): T {
    return Globals.gson.fromJson(this, object : TypeToken<T>() {}.type)
}

这个函数就是完美的,

你可以直接传入fromJson<List<Bean>(), 可以传入fromJson<CmdBean<InnerBean>()的形式,他就能给你处理好。
而1.8.10的kotlin,在inline+reified嵌套后就会出现问题。
大于1.8.10的kotlin版本,则可以嵌套传导正确,就不会出现问题了。

而非kotlin的java项目,你就不得不按照官方

java 复制代码
public static <T> T parse(String jsonStr, Class<T> t) {
    return new GsonBuilder().create().fromJson(jsonStr, t);
}

public static <T, T2> MyCmd<T2> parseLv2(String jsonStr, Class<T> t, Class<T2> t2) {
    Type typeToken = TypeToken.getParameterized(t, t2).getType();
    return new GsonBuilder().create().fromJson(jsonStr, typeToken);
}

或者参考我上篇帖子https://blog.csdn.net/jzlhll123/article/details/139302991的三个解决方案。

java中就不会遇到kotlin编译内部类的问题了。而是可能会犯低级错误:

比如

java 复制代码
        var json = """
{"apiBean":{"name":"nameX","type":"typeX","url":"htpps://api.....com/.../..xxx"},"cmdId":"cmdId010102394","pro":"aaaa","ts":1029301291023}
        """;
        var cmd = parse(json, CmdBean.class); //错误,没有提供二级泛型。需要TypeToken.getParameterized等方式解析。
        System.out.printf("cmd " + cmd);

		val listCmd = parse(json, List.class) //错误,没有提供二级泛型。需要TypeToken.getParameterized等方式解析。

而kotlin,由于inline和reified就可以解决它,前提是你要注意kotlin的版本哟,1.8.10有坑!1.8.20开始好的。

相关推荐
alexhilton31 分钟前
Compose Unstyled:Compose UI中失传的设计系统层
android·kotlin·android jetpack
刘龙超2 小时前
如何应对 Android 面试官 -> 玩转 RxJava (基础使用)
android·rxjava
柿蒂3 小时前
从动态缩放自定义View,聊聊为什么不要把问题复杂化
android·ai编程·android jetpack
kerli3 小时前
kotlin协程系列:callbackFlow
android·kotlin
没有了遇见5 小时前
Android 原生定位实现(替代融合定位收费,获取经纬度方案)
android·kotlin
一枚小小程序员哈5 小时前
基于Android的车位预售预租APP/基于Android的车位租赁系统APP/基于Android的车位管理系统APP
android·spring boot·后端·struts·spring·java-ee·maven
诸神黄昏EX5 小时前
Android SystemServer 系列专题【篇四:SystemServerInitThreadPool线程池管理】
android
用户2018792831676 小时前
pm path 和 dumpsys package 的区别
android
是店小二呀6 小时前
【C++】智能指针底层原理:引用计数与资源管理机制
android·java·c++
DoubleYellowIce7 小时前
一次混淆XLog导致的crash分析记录
android