Kotlin中多Elvis运算符未返回默认值却为null的原因
问题解析:Gson实例化后Elvis运算符的不同表现
场景还原
先定义Kotlin数据类:
data class Example( @SerializedName("id") val id: String, @SerializedName("url") val url: String, @SerializedName("more") val more: String = "", )
用Gson从缺少url字段的JSON字符串实例化对象:
val example1 = Gson().fromJson("{id: 1}", Example::class.java) val example2 = Gson().fromJson("{id: 2}", Example::class.java)
因为JSON里没有url字段,Gson会通过反射给这两个实例的url赋值为null——这绕过了Kotlin的非空类型检查。
奇怪的现象
执行下面代码时,输出结果是url:null:
val url: String = example1?.url ?: example2?.url ?: "aaaaaaaaaa" println("url:$url")
但换成直接用.访问字段,却能输出预期的默认值url:aaaaaaaaaa:
val url: String = example1.url ?: example2.url ?: "aaaaaaaaaa" println("url:$url")
原因拆解
问题出在Kotlin编译器对?.操作符的类型推断,以及Elvis运算符(?:)的编译逻辑:
?.带来的编译器优化:example1的编译时类型是Example(非空),所以example1?.url等价于直接用.访问,但编译器会推断这个表达式的类型是String(非空)——也就是默认它不可能返回null。基于这个推断,编译器会跳过Elvis运算符的后续分支,直接取example1?.url的运行时值(也就是null),所以最终结果是null。- 直接用
.的运行时检查:
虽然example1.url的编译时类型也是非空String,但Elvis运算符在运行时会直接检查实际值是否为null。当发现example1.url是null时,会依次检查example2.url,最后触发默认值分支,返回"aaaaaaaaaa"。
说白了就是Gson搞出来的运行时null,和编译器基于类型推断做的优化撞了车。
内容的提问来源于stack exchange,提问作者Garfieldmao
相关产品推荐
相关产品推荐

