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

相关推荐
pengyu17 分钟前
【Kotlin系统化精讲:叁】 | 变量与常量:自由与约束的代码博弈
android·kotlin
ilmari37 分钟前
HarmonyOS 基于Network Kit封装的网络请求工具
android·flutter·harmonyos
Sugobet6 小时前
【安卓][Mac/Windows】永久理论免费 无限ip代理池 - 适合临时快速作战
android·tcp/ip·macos·网络安全·渗透测试·ip代理池·接入点
fatiaozhang952710 小时前
创维智能融合终端SK-M424_S905L3芯片_2+8G_安卓9_线刷固件包
android·电视盒子·刷机固件·机顶盒刷机
来来走走11 小时前
Flutter开发 了解Scaffold
android·开发语言·flutter
哆啦A梦的口袋呀12 小时前
Android 底层实现基础
android
闻道且行之12 小时前
Android Studio下载及安装配置
android·ide·android studio
alexhilton13 小时前
初探Compose中的着色器RuntimeShader
android·kotlin·android jetpack
小墙程序员13 小时前
kotlin元编程(二)使用 Kotlin 来生成源代码
android·kotlin·android studio
小墙程序员13 小时前
kotlin元编程(一)一文理解 Kotlin 反射
android·kotlin·android studio