Android 系统上崩溃率最高的异常类型就是空指针异常(NullPointerException)。Kotlin 默认所有的参数和变量都不可为空,相比于 Java,Kotlin 将空指针异常检查提前到了编译时期。除此之外,Kotlin 提供了一套可为空的类型系统及判空辅助工具。
var name: String = null // 这样写会报错:Null can not be a value of a non-null type String
?
var name: String? = null
这种类型之后加 ? 的写法,Kotlin 中叫可空类型。比如,String 表示不可空的字符串,而 String? 表示可为空的字符串。
不过,当我们使用了可空类型后,在 Kotlin 中会有新的问题:
var view: View? = null
view.setBackgroundColor(Color.BLACK) // 这样写会报错:Only safe (?.) or non-null asserted (!!.) calls are allowed on a nullable receiver of type View?
由于对空引用的调用会导致空指针一次样,可空类型在 Kotlin 无法编译通过。
我们可以尝试如下解决:
var view: View? = null
if (view != null) {
view.setBackgroundColor(Color.BLACK)
}
上面的问题貌似解决了,但是如果 view 是全局变量,又会有新的问题:
var view: View? = null
fun setColor() {
if (view != null) {
view.setBackgroundColor(Color.BLACK) // 这里会报错:Smart cast to 'View' is impossible, because 'view' is a mutable property that could have been changed by this time
}
}
这个报错的意思是即使你检查了非空也不能保证下面调用的时候就是非空,因为在多线程情况下,其他线程可能把它再改成空的。
为此,Kotlin 提供了一系类的辅助工具,是开发者能够更轻松地进行判空处理。
?.
当对象不为空时正常调用相应的方法,当对象为空时什么都不做。
view?.setBackgroundColor(Color.BLACK)
这个写法同样会对变量做一次非空确认之后再调用方法,这是 Kotlin 的写法,并且它可以做到线程安全,因此这种写法叫做「safe call」。
?:
这个操作符的左右两边都接收一个表达式,如果左边表达式的结果不为空就返回左边表达式的结果,否则就返回右边表达式的结果。
view = text ?: null
接下来列举一个例子结合使用 ?. 和 ?: 这两个操作符,感受一下它们的强大。
首先用传统的写法来写:
fun getTextLength(text: String?): Int {
if (text != null) {
return text.length
}
return 0
}
借助操作符之后:
fun getTextLength(text: String?) = text?.length ?: 0
!!
非空断言工具。
view!!.setBackgroundColor(Color.BLACK)
这是一种由风险的写法,意思是告诉编译器,我保证这里的 view 一定是非空的,编译器你不要帮我做空指针检查了,有什么后果我自己承担。一旦用了非空断言,实际上和 Java 就没什么两样了,但也就享受不到 Kotlin 的空安全设计带来的好处(在编译时做检查,而不是运行时抛异常)了。所以在使用这个工具的时候,最好提醒一下自己,是不是还有更改好的实现方式。
let
let 是一个函数,属于 Kotlin 中的标准函数。这个函数提供了函数式 API 的编程接口,并将原始调用对象作为参数传递到 Lambda 表达式中:
obj.let { obj2 ->
// 编写具体的业务逻辑
}
let 函数的特性配合 ?. 操作符可以在空指针检查的时候起到很大的作用。比如下面的代码:
fun initTextView(text: TextView?) {
text?.setBackgroundColor(Color.BLACK)
text?.setTextColor(Color.WHITE)
}
上面的代码实际上是进行了两次的判空操作,可以使用 let 函数对其进行优化:
fun initTextView(text: TextView?) {
text?.let {
it.setBackgroundColor(Color.BLACK)
it.setTextColor(Color.WHITE)
}
}
上述代码中的 it 出现的规则是,当 Lambda 表达式的参数列表中只有一个参数时,可以不用声明参数名,直接使用 it 关键字来代替即可。