我们好,本篇文章共享一下之前运用gson和kotlin碰撞出的一些火花,脑瓜子被整的懵懵的那种。
准备常识
总所周知,当Gson没有无参结构函数时,会运用UnSafe以一种非安全的办法去创立类的目标,这样会产生两个问题:
- 特点的默许初始值会丢掉,比如某个类中有这么一个特点
public int age = 100
,经过unsafe创立该类目标,会导致age
的默许值100丢掉,变为0;
- 会绕过Kotlin的空安全查验,由于经过unsafe创立的目标不会在特点赋值时进行可null校验。
所以一般比较在运用Gson反序列化时,比较引荐的做法便是反序列化的类要有无参结构函数。
PS:其实供给了无参结构函数,仍是有可能会绕过Kotlin空安全校验,毕竟在Gson中特点是经过反射赋值的,所以一些人会引荐运用Moshi,这个笔者还没怎样运用过,后续会了解下。
看一个脑瓜子懵的例子
先上代码:
class OutClass {
val age: Int = 555
override fun toString(): String {
return "OutClass[age = $age]"
}
inner class InnerClass {
val age1: Int = 897
override fun toString(): String {
return "InnerClass[age = ${this.age1}]"
}
}
}
以上两个类OutClass
和InnerClass
看起来都有无参结构函数,现在我们来对其进行逐个反序列化。
1. 反序列化OutClass
fun main(args: Array<String>) {
val content = "{"content": 10}"
val out = OutClass::class.java
val obj = Gson().fromJson(content, out)
println(obj)
}
反序列化运用的字符串是一个OutClass
类不存在的特点content
,我们看下输出成果:
看起来没缺点,由于存在无参结构函数,且反序列化所运用的字符串也不包括age
字段,age
的默许值555得以保存。
2. 反序列化InnerClass
先上测试代码:
fun main(args: Array<String>) {
val content = "{"content": 10, "location": null}"
val out = OutClass.InnerClass::class.java
val obj = Gson().fromJson(content, out)
println(obj)
}
运转成果如下:
不是InnerClass
也是有无参结构函数的吗,为啥age
字段的默许值897没有被保存,其时给整蒙了。
所以进行了下debug断点调试,发现最终是经过Unsafe创立了InnerClass
:
其时是百思不得其解,后续想了想,非静态内部类本身会持有外部类的引证,而这个外部类的引证是经过内部类的结构办法传入进来的,我们看一眼字节码:
所以非静态内部类根本就没有无参结构办法,所以最终经过Gson反序列化时自然便是经过Unsafe创立InnerClass
目标了。
假如想要解决上面这个问题,将非静态内部类改成静态内部类就行了,或许尽量避免运用非静态内部类作为Gson反序列化的类。
另外我们假如感兴趣想要了解下Gson是如何判别的反射无参结构办法仍是走Unsafe创立目标的,能够看下源码:
ReflectiveTypeAdapterFactory#create ——>ConstructorConstructor#get
介绍下typeOf()
办法
回忆下我们之前是怎样反序列化调集的,看下下面代码:
fun main(args: Array<String>) {
val content = "[{"content": 10, "location": "aa"}, {"content": 10, "location": "bb"}]"
val obj = Gson().fromJson<List<OutClass>>(content, object : TypeToken<List<OutClass>>(){}.type)
println(obj)
}
要创立一个很费事的TypeToken
目标,获取其type
然后再进行反序列化,输出如下正确成果:
为了避免费事的创立TypeToken
,我之前写了一篇文章来优化这点,我们感兴趣的能够看下这篇文章:Gson序列化的TypeToken写起来太费事?优化它
然后之前有个掘友评论了另一个官方供给的解决办法:
所以我赶忙试了下:
@OptIn(ExperimentalStdlibApi::class)
fun main(args: Array<String>) {
val content = "[{"content": 10, "location": "aa"}, {"content": 10, "location": "bb"}]"
val obj = Gson().fromJson<List<OutClass>>(content, typeOf<List<OutClass>>().javaType)
println(obj)
}
运转输出:
没缺点,这个写法要比创立一个TypeToken
简单多了,这个api是很早就有了,不过到了kotlin1.6.0插件版别才稳定的,请我们注意下这点:
十分引荐我们运用这种办法,官方支撑,就杰出一个字:稳。
总结
本篇文章主要是给我们介绍了Gson反序列化非静态内部类时的坑,以及介绍了一个官方支撑的api:typeOf()
,协助我们简化反序列化调集的操作,期望本篇文章能比照有所协助。
历史文章
两个Kotlin优化小技巧,你肯定用的上
Kotlin1.9.0-Beta,它来了!!
Kotlin1.8新增特性,进来了解一下
聊聊Kotlin1.7.0版别供给的一些特性
聊聊kotlin1.5和1.6版别供给的一些新特性
kotlin密封sealed class/interface的迭代之旅
优化@BuilderInference注解,Kotlin高版别下了这些“毒手”!
@JvmDefaultWithCompatibility优化小技巧,了解一下~