1、前语

本文将会经过详细的业务场景,由浅入深的引出Kotlin的一个bug,并奉告咱们这个bug的共同之处,接着会带领咱们去查找bug呈现的原因,究竟去躲避这个bug。

json格局怎样翻开插曲

本人正在参加2020年度人气创作者榜单排名,期望各位小伙伴帮我投票,2021年会给咱们带来更多优质的文章。

2、bug复现

application践开发中,咱们常常会有将Json字符串反序列化为一个政策问题,这儿,咱们用Gsonjvm优化来写一段反序列代码,如下:

fun <T> fromJson(json: String, clazz: Class<T>): T? {
return try {
Gson().fromJson(json, clazz)
} catch (ignore: ExcepHTTPtion) {
null
}
}

以上代码,仅适用于不带泛型的类,关于带泛型的类,如List<T>,咱们就要再改造一下,如下:

fun <T> fromJson(json: String, type: Type): T? {
return try {
return Gson().fromJson(json, typjvm内存模型e)
} catch (e: Exception) {
null
}
}

此刻http署理kotlin极简教程咱们就能够仰仗于Gson里面的TypeToken类,然后结束任意类型的反序列化,如下:

//1、反序列化User政策
val user: User? = fromJson("{...}}", User::cljvm调优ass.jaJSONva)
//2、反序列化Lijsonpst<User>政策,其它带有泛kotlin实战型的类,皆可用此办法序列化
val type = object : TypeToken<List<User>>() {}.type
val users: List<User>? = fromJson("[{..},{...}]", type)

以上写法,是Java的语法翻译过来的,它有一个缺陷,那便是泛型的传递必需求经过另一个类去结束,上面咱们仰仗类TypeToken类,信赖这一点,很多人都不能承受,于是乎,在Kotlin上,呈现了一个新的关键字reified(这儿不翻开介绍,不了解的自行查阅相关材料),它结合kotlin的内联(inline)函数的特性,便能够直接在办法内部获取详细的泛型类型,咱们再次把上面的办法改造下,如下:

inline fun <reified T> fromJson(jjvm参数详解soJVMn: String): T? {
return try {
return Gson().fromJson(jsjvm废物收回机制onjson数据格局, T::clajsonobjectss.java)
} catch (e: Exception) {
null
}
}

能够看到,咱们在办法前加上了inline关键字,标明这是一个内联函数;接着在泛型T前面加上reified关键字,并把办法里不需求的Type参数去掉;究竟appstore咱们经过T::class.jkotlin与javaava传递详细的泛型类型,详细运用如下:

val user = fromJson<User>("{...}}")
val users = fromJson<List<User>>("[{..},{...}]")

当咱们满怀信心的检验以上代码时,问题出appreciate现了,List<User>反序列化失利了,如下:

发现Kotlin一个奇特的bug

List里面的政策竟不是Userjson解析,而是LinkedTreeMap,怎样回事,这莫非便是标题所说的Kotlin的bug?当然不是!

咱们回到fromJshttpclienton办法中,看到内部传递的jvm原理T::clahttp署理ss.java政策,即class政策,而class政策有泛型的话,在运转期间泛型会被擦除,故假定是List<User>政策,运转期间就变成了Lisjvm是什么意思t.class政策,而Gson在收到的泛型不明确时,便会主动将json政策反序JSON列化为Linkedkotlin实战TreeMapHTTP针。

怎样处理?好办,我jvm是什么意思们仰仗Tyjvm废物收回机制peToken类传递泛型即可,而这次,咱们仅需求在办法内部写一次即可,如下:

inline fun <reified T> fromJson(jsonkotlin发音: String): T? {
rjsonejvm废物收回机制turn try {
//仰仗TypeTokjvm调优面试题en类获取详细的泛型类型
val type = object : TypeToken<T>() {}.type
return Gson().fromJson(json, type)
} catch (e: Exception) {app装置下载
null
}
}

此刻,咱们再来检验下上述的代码,如下:

发现Kotlin一个奇特的bug

能够看到,这次不管是User,仍是List<User>政策,都反序列化成功了。

到此,有人会jvm调优面试题有疑问,叨叨了这么多,说好的Kotlin的bug呢?别着急,持续往下看,bug就快要呈现了。

遽然jvm内存模型有一天,你的leader过来跟你说,这个fromJsjson是什么意思on办法还能不能再优化一下,现在每kotlin教程次反序列化Lkotlin面试题ist集结,都需求在fjvm调优参数romJson后写上<List<>>,这种场景十分多,写起来略微有点繁琐。

此刻你kotlin ?.作用jvm内存模型里一万个那啥蹦腾而http 302过,不过静下来想想,leader说的也并不是appreciate没有道理,假定遇到多层泛型的情况,写起来就会更加繁琐,如:fromJson<BaseResponshttpcliente<List<User>>>,

于是就敞开了优化之路,把常用的泛型类进行解耦,究竟,你写出了如下代码:

inline fun <reified T> fromJson2List(json: String) = fromJson<List<T>>(json)

检验下,咦Kotlin?惊呆了,似曾相识的问题,如下:

发现Kotlin一个奇特的bug

这又是为什么?fromJson2List内部仅调用了fromJson办法,为啥fromJson能够,fromJson2List却失利了,百思不得其解。

莫非这便是标题说的Kotlin的bug?很负责任的告知你,是的;

bug共同在哪里?持续往下看

3、bug的共同之处

咱们重新整理下整个事件,上面咱们先定义了两个办法,把它们放到Json.kt文件中,无缺代码如下:

@file:JvmName("Jsonappleid")
package com.exampkotlin极简教程le.test
import com.google.gson.Gson
import com.google.gson.reflect.TypeToken
inline fjvm参数详解un <reified Tjvm参数详解> fromJson2List(APPjson: Stringjson是什么意思) =appstore fromJson<Lisjvm原理t&jvm内存模型lt;T>>(json)
inline fun <reified T&http 302gt; fromJsjson是什么意思on(json: String): T? {
return tryapplication {
val type = object : TypeToken<T>() {}.tykotlin为什么盛行不起来pe
return Gson().fromJson(json, type)
}approach catch (e: Exception) {
null
}
}

接着新建User类,无缺代码如下:

pappstoreackage com.example.bean
class User {
val name: String? = null
}

随后又新建一个JsonTest.kt文件,结束代码如下:

@file:JvmName("JsonTest")
package com.example.test
fun maikotlin言语n() {
vjson文件是干什么的al user = fromJson<User>("""{"nappreciateame": "张三"}""")
val users = fromJson<List<User>>("""[{"name": "张三"}json格局,{"name": "李四"}]""")
val userList = fromJson2List<User>("""[{approve"name": "张三"},{"name": "李四"}]""")
print("")
}

留意:这3个类在同一个jvm调优包名下,且在同一个Module中jvm调优参数

究竟实施main办法,就会发现所说的bug。

留意,kotlin发音前方高能:咱们把Json.kt文件拷贝一approach份到json是什么意思Bajson格局怎样翻开se Modukotlin极简教程lJVMe中,如下:

@file:JvmName("Json")
package com.example.base
import com.google.gson.Gson
import com.googlekotlin面试题.gson.reappearanceflect.TypeToken
inline fun <reified T> fromJson2appleidList(json: String) = fromJson&kotlin教程lt;List&lAPPt;T>>(json)
inline fun <reified T&gjsont; fromJson(json: Stkotlin下载ringhttp署理)httpclient: T? {
return try {
val type = object : TypeToken<T>() {}.type
return Gson().fromJson(jsjvm废物收回机制on, type)
} catch (e: Exception) {
null
}
}

随后咱们在app module里的Json.kt文件中参加一个检验办法,如下:

fun test()httpclient {
val users = fromJson2List<User>("""[{json"name": "张三"},{"name": "李四"}]""")
val userListappearance = com.example.base.fromJson2List<User>json文件是干什么的("""[{"name": "张三"},{"name": "李四"}kotlin面试题]""")
print("")
}

注:在jvm优化basapprovee module里的Json.kt文件中没有这个办法

上面代码中,别离实施了app modulebase module中的fromJson2Lisjson数组t办法,咱们来猜一猜上面代application码实施的预期效果

第一条语句,有了上面的事例,明显会回来List<LinkedTreeMap>政策;那第二条jvm调优面试题呢?按道理也应该回来List<LinkedTreeMap&approvegt;政策,然而,适得其反,实施下看看jvm是什么意思,如下:

发现Kotlin一个奇特的bug

能够看到,app modulefromJson2List 办法反序列化List<User>approve利了,而base module中的fromJson2List 办法却成功了。

json同的代码,只是地址module不一样,实施效果也不一样,你说神不共同?

4、一探求竟

知道bug了,也appear知道了bug的共同之处,接下来就去探求下,为什么会这样?从approve哪下手?

明显,要去看Json.kt类的字节码文件,咱们先来看看base module里的Json.class文件,如下:

注:以下字节码文件,为便当检查,会删去一些json是什么意思注解信息

package com.example.base;
import com.google.gson.reflect.TypeToken;
import java.util.List;
public final class Json {
public static final clasappreciates Json$fromJjvm废物收回机制son$type$1 extends TypeToken<T> {jvm调优}
public stajvm优化tic final claapplicationss Json$fromJson2List$$inlined$fromhttp署理Json$1 extends TypeToken<List<? extends T>> {}
}

能够看到,Json.kt里面的两个内联办法,编译为字节码文件后,变成了两个静态内部类,且都承继了TypeToken类,看起来没啥问题,

持续看看app moduleJson.kt文件对应的字节码文件,如下:

package com.exjvm优化ample.test;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
ijson数据格局mport java.lahttpwatchng.reflect.Type;
import java.utiljson格局.List;
public final class Json {
puhttp协议blic static final void test() {
List list;
Object object = null;
try {
Type type = (new Json$fromJson2List$$inlined$fromJson$2()http协议).getType();
list = (app装置下载List)(new Gson()).fromJson("[{"name": ""},{"name": ""}]", thttp 302ype);
} catch (Exception exception) {
lishttps和http的差异t = null;
}
(List)liJVMst;
try {
Type type = (new Jhttps和http的差异son$test$$inlined$fromJson2List$1()).getType();
object = (new Gson()).fromJson("[{"namjvm原理e": ""},{"name": ""}]", type);
} catch (Excjvm原理eption exception) {https和http的差异}
(List)object;
SyHTTPstem.outappearance.print("");
}
public static final class Json$frhttpwatchomJson$type$1 extendjson数据格局s TypeToken<T> {}
public static final class Json$fromJson2List$$inlined$fromJson$1 extends TypeToken<List<? extejvm内存结构nds T>> {}
public static final clahttp协议ss Json$fromJson2List$$inlined$fromJson$2 extends TypeToken<Lishttp 302t<? extends T>>jsonobject {}
public static final class Jkotlin为什么盛行不起来son$test$$inlined$fromJson2List$1 extends TypeToken<Lijson数组st<? extends User>> {}
}

在该字节码文件中,有1个testappear办法 + 4个静态内kotlin与java部类;前apple两个静态内部类,便是Json.kt文件中两个内联办法编译后的效果,这个能够不必管。kotlin言语

接着,来看看test办法,该办法有两次反序列化进程,第一次调用了静态内部类Jsonfrohttp 302mJson2List$$inlinedfromJson$2,第二次调用了静态内部类Jsontest$$inlinedfromJson2List$1,也便是别appearappear调用了HTTP第三、第四个静态内部类去获取详细的泛型类型,而这两个静态内部类声明的泛型类型是不一样的,别离是&ltappstore;List<? extends T>><List<? extends User>>,到这,估量大伙都理解了,明显第一次反序列kotlin实战化进程泛型被擦除了,所以导致了反序列化失利。

至于为什么依托本module的办法,遇到泛型T与详细类相结合时http 404,泛型T会被擦除问题,这个就需求Kohttp署理tlin官网来kotlin ?.作用回答了,有知道原因的小伙伴,能够在议论区留言。

5、扩展

假定你的项目没有依托Gson,能够自定义一个类,来获取详细的泛型类型,如下:

open class TypeLiteral<T> {
val typeapp装置下载: Type
get() = (javaClass.genericSuperclass as Parameterijvm调优参数zedType).actualTypeArgumjvm调优面试题ents[0]
}
//用以下代码替换TypeToken类相http署理关代码即可
val type = objecapplicationt : TypappreciateeLiteral<T&gthttp 404;() {}.type

关于泛型的组合,还能够用RxHttp库里面的ParameterizedTypeImpl类,用法如下:

//得到 List<User> 类型
val type: Typekotlin极简教程 = Pakotlin为什么盛行不起来rameterizedTypeImpl[List::class.java, User::class.java]

详细用法可检查appreciateAndroid、Java泛型扫盲

6、小结

现在要躲避这个问题的话,将相关代码移动到子moJVMduljsonobjecte即可json文件是干什么的,调用子module代码json数据格局就不会有泛型擦除问题;

这个问题,其实在kotlin 1.3.x版别时,我就发现了json解析,到现在最新版别也一贯存在,期间曾请教过Bennyhuo大神,后边躲避了这个问题,就没放json数组心上,近期将会把这个问题,提交给kotlin官方,望赶快批改。

各位小伙伴,假定本文对你有帮助,记住给我投票,感谢咱们! 2020年度人气创作者榜单排名,帮助给我投票,2021年会给咱们带来更多优质的文章。

究竟,给咱们推荐一个网络央求库RxHttp,支撑Kotlin协程、RxJava2、RxJava3,任意央求三步搞定,截止现在已有2.7k+ star,真的很jsonp不错的一个库,剧烈推荐