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开始好的。

相关推荐
GEEKVIP1 小时前
手机使用技巧:8 个 Android 锁屏移除工具 [解锁 Android]
android·macos·ios·智能手机·电脑·手机·iphone
model20053 小时前
android + tflite 分类APP开发-2
android·分类·tflite
彭于晏6893 小时前
Android广播
android·java·开发语言
与衫4 小时前
掌握嵌套子查询:复杂 SQL 中 * 列的准确表列关系
android·javascript·sql
500了10 小时前
Kotlin基本知识
android·开发语言·kotlin
人工智能的苟富贵11 小时前
Android Debug Bridge(ADB)完全指南
android·adb
小雨cc5566ru16 小时前
uniapp+Android面向网络学习的时间管理工具软件 微信小程序
android·微信小程序·uni-app
bianshaopeng17 小时前
android 原生加载pdf
android·pdf
hhzz17 小时前
Linux Shell编程快速入门以及案例(Linux一键批量启动、停止、重启Jar包Shell脚本)
android·linux·jar
火红的小辣椒18 小时前
XSS基础
android·web安全