return try {
//借助TypeToken类获取具体的泛型类型
val type = object : TypeToken() {}.type
return Gson().fromJson(json, type)
} catch (e: Exception) {
null
}
}
此时,我们再来测试下上述的代码,如下:
可以看到,这次不管是User
,还是List<User>
对象,都反序列化成功了。
到此,有人会有疑问,叨叨了这么多,说好的Kotlin的bug呢?别着急,继续往下看,bug就快要出现了。
突然有一天,你的leader过来跟你说,这个fromJson
方法还能不能再优化一下,现在每次反序列化List
集合,都需要在fromJson
后写上<List<>>
,这种场景非常多,写起来略微有点繁琐。
此时你心里一万个那啥蹦腾而过,不过静下来想想,leader说的也并不是没有道理,如果遇到多层泛型的情况,写起来就会更加繁琐,如:fromJson<BaseResponse<List<User>>>
,
于是就开启了优化之路,把常用的泛型类进行解耦,最后,你写出了如下代码:
inline fun fromJson2List(json: String) = fromJson<List>(json)
测试下,咦?惊呆了,似曾相识的问题,如下:
这又是为什么?fromJson2List
内部仅调用了fromJson
方法,为啥fromJson
可以,fromJson2List
却失败了,百思不得其解。
难道这就是标题说的Kotlin的bug?很负责任的告诉你,是的;
bug神奇在哪里?继续往下看
3、bug的神奇之处
我们重新梳理下整个事件,上面我们先定义了两个方法,把它们放到Json.kt
文件中,完整代码如下:
@file:JvmName(“Json”)
package com.example.test
import com.google.gson.Gson
import com.google.gson.reflect.TypeToken
inline fun fromJson2List(json: String) = fromJson<List>(json)
inline fun fromJson(json: String): T? {
return try {
val type = object : TypeToken() {}.type
return Gson().fromJson(json, type)
} catch (e: Exception) {
null
}
}
接着新建User
类,完整代码如下:
package com.example.bean
class User {
val name: String? = null
}
随后又新建一个JsonTest.kt
文件,完成代码如下:
@file:JvmName(“JsonTest”)
package com.example.test
fun main() {
val user = fromJson(“”“{“name”: “张三”}”“”)
val users = fromJson<List>(“”“[{“name”: “张三”},{“name”: “李四”}]”“”)
val userList = fromJson2List(“”“[{“name”: “张三”},{“name”: “李四”}]”“”)
print(“”)
}
注意:这3个类在同一个包名下,且在同一个Module中
最后执行main方法,就会发现所说的bug。
注意,前方高能:我们把Json.kt
文件拷贝一份到Base Module
中,如下:
@file:JvmName(“Json”)
package com.example.base
import com.google.gson.Gson
import com.google.gson.reflect.TypeToken
inline fun fromJson2List(json: String) = fromJson<List>(json)
inline fun fromJson(json: String): T? {
return try {
val type = object : TypeToken() {}.type
return Gson().fromJson(json, type)
} catch (e: Exception) {
null
}
}
随后我们在app module
里的Json.kt
文件中加入一个测试方法,如下:
fun test() {
val users = fromJson2List(“”“[{“name”: “张三”},{“name”: “李四”}]”“”)
val userList = com.example.base.fromJson2List(“”“[{“name”: “张三”},{“name”: “李四”}]”“”)
print(“”)
}
注:在base module里的Json.kt文件中没有这个方法
上面代码中,分别执行了app module
和base module
中的fromJson2List
方法,我们来猜一猜上面代码执行的预期结果
第一条语句,有了上面的案例,显然会返回List<LinkedTreeMap>
对象;那第二条呢?按道理也应该返回List<LinkedTreeMap>
对象,然而,事与愿违,执行下看看,如下:
可以看到,app module
中fromJson2List
方法反序列化List<User>
失败了,而base module
中的fromJson2List
方法却成功了。
同样的代码,只是所在module不一样,执行结果也不一样,你说神不神奇?
4、一探究竟
知道bug了,也知道了bug的神奇之处,接下来就去探索下,为什么会这样?从哪入手?
显然,要去看Json.kt
类的字节码文件,我们先来看看base module
里的Json.class
文件,如下:
注:以下字节码文件,为方便查看,会删除一些注解信息
package com.example.base;
import com.google.gson.reflect.TypeToken;
import java.util.List;
public final class Json {
public static final class Json f r o m J s o n fromJson fromJsontype$1 extends TypeToken {}
public static final class Json
f
r
o
m
J
s
o
n
2
L
i
s
t
fromJson2List
fromJson2List
i
n
l
i
n
e
d
inlined
inlinedfromJson$1 extends TypeToken<List<? extends T>> {}
}
可以看到,Json.kt
里面的两个内联方法,编译为字节码文件后,变成了两个静态内部类,且都继承了TypeToken
类,看起来没啥问题,
继续看看app module
的Json.kt
文件对应的字节码文件,如下:
package com.example.test;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import java.lang.reflect.Type;
import java.util.List;
public final class Json {
public static final void test() {
List list;
Object object = null;
try {
Type type = (new Json
f
r
o
m
J
s
o
n
2
L
i
s
t
fromJson2List
fromJson2List
i
n
l
i
n
e
d
inlined
inlinedfromJsonKaTeX parse error: Got function '\"' with no arguments as argument to '\"' at position 67: …"[{\"name\": \"\̲"̲},{\"name\": \"…test$
i
n
l
i
n
e
d
inlined
inlinedfromJson2List$1()).getType();
object = (new Gson()).fromJson(“[{“name”: “”},{“name”: “”}]”, type);
} catch (Exception exception) {}
(List)object;
System.out.print(“”);
}
public static final class Json f r o m J s o n fromJson fromJsontype$1 extends TypeToken {}
public static final class Json f r o m J s o n 2 L i s t fromJson2List fromJson2List i n l i n e d inlined inlinedfromJson$1 extends TypeToken<List<? extends T>> {}
public static final class Json f r o m J s o n 2 L i s t fromJson2List fromJson2List i n l i n e d inlined inlinedfromJson$2 extends TypeToken<List<? extends T>> {}
public static final class Json
t
e
s
t
test
test
i
n
l
i
n
e
d
inlined
inlinedfromJson2List$1 extends TypeToken<List<? extends User>> {}
}
在该字节码文件中,有1个test方法 + 4个静态内部类
;前两个静态内部类,就是Json.kt
文件中两个内联方法编译后的结果,这个可以不用管。
接着,来看看test方法,该方法有两次反序列化过程,第一次调用了静态内部类JsonfromJson2List$$inlinedfromJson$2
,第二次调用了静态内部类Jsontest$$inlinedfromJson2List$1
,也就是分别调用了第三、第四个静态内部类去获取具体的泛型类型,而这两个静态内部类声明的泛型类型是不一样的,分别是<List<? extends T>>
和<List<? extends User>>
,到这,估计大伙都明白了,显然第一次反序列化过程泛型被擦除了,所以导致了反序列化失败。
至于为什么依赖本module的方法,遇到泛型T
与具体类相结合时,泛型T
会被擦除问题,这个就需要Kotlin官网来解答了,有知道原因的小伙伴,可以在评论区留言。
5、扩展
如果你的项目没有依赖Gson
,可以自定义一个类,来获取具体的泛型类型,如下:
open class TypeLiteral {
val type: Type
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则近万的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Android)
![](https://img-blog.csdnimg.cn/img_convert/91ac9c84e6c01e7bf106ef6abf5633d0.jpeg)
最后
Android学习是一条漫长的道路,我们要学习的东西不仅仅只有表面的 技术,还要深入底层,弄明白下面的 原理,只有这样,我们才能够提高自己的竞争力,在当今这个竞争激烈的世界里立足。
人生不可能一帆风顺,有高峰自然有低谷,要相信,那些打不倒我们的,终将使我们更强大,要做自己的摆渡人。
资源持续更新中,欢迎大家一起学习和探讨。
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!
明白下面的 原理,只有这样,我们才能够提高自己的竞争力,在当今这个竞争激烈的世界里立足。
人生不可能一帆风顺,有高峰自然有低谷,要相信,那些打不倒我们的,终将使我们更强大,要做自己的摆渡人。
资源持续更新中,欢迎大家一起学习和探讨。
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!