一、空安全
1.1、可空性
在 Kotlin 中,类型系统区分一个引用是可以容纳 null (可空引用)还是不能容纳(非空引
用)。 例如,String 类型的常规变量不能指向 null
var name: String = "leavesC"
//编译错误
//name = null
如果希望一个变量可以储存 null 引用,需要显式地在类型名称后面加上问好来标记它:
var name: String? = "leavesC"
name = null
问号可以加在任何类型的后面来表示这个类型的变量可以存储 null 引用:Int?、Doubld? 、Long? 等
Kotlin 对可空类型的显式支持有助于防止 NullPointerException 导致的异常问题,编译器不允许不对可空变量做 null 检查就直接调用其属性
fun check(name: String?): Boolean {
//编译器不允许不对 name 做 null 检查就直接调用其属性
return name.isNotEmpty()
}
需要显式地进行 null 检查
fun check(name: String?): Boolean {
if (name != null) {
return name.isNotEmpty()
}
return false
}
1.2、安全调用运算符:?.
安全调用运算符:?. 允许把一次 null 检查和一次方法调用合并为一个操作,如果变量值非空,则方法或属性会被调用,否则直接返回 null
例如,以下两种写法是完全等同的:
fun check(name: String?) {
if (name != null) {
println(name.toUpperCase())
} else {
println(null)
}
}
fun check(name: String?) {
println(name?.toUpperCase())
}
1.3、Elvis 运算符:?:
Elvis 运算符:?: 用于替代 ?. 直接返回默认值 null 的情况,Elvis 运算符接收两个运算数,如果第一个运算数不为 null ,运算结果就是其运算结果值,如果第一个运算数为 null ,运算结果就是第二个运算数
例如,以下两种写法是完全等同的:
fun check(name: String?) {
if (name != null) {
println(name)
} else {
println("default")
}
}
fun check(name: String?) {
println(name ?: "default")
}
1.4、安全转换运算符:as?
安全转换运算符:as? 用于把值转换为指定的类型,如果值不适合该类型则返回 null
fun check(any: Any?) {
val result = any as? String
println(result ?: println("is not String"))
}
1.5、非空断言:!!
非空断言用于把任何值转换为非空类型,如果对 null 值做非空断言,则会抛出异常
fun main(args: Array<String>) {
var name: String? = "leavesC"
check(name) //7
name = null
check(name) //KotlinNullPointerException
}
fun check(name: String?) {
println(name!!.length)
}
1.6、let 函数
let 函数可用于在表达式不为 null 时才执行指定代码块
fun main(args: Array<String>) {
var name: String? = "leavesC"
check(name) //leavesC
name = null
check(name) //什么都不会输出
}
fun check(name: String?) {
name?.let {
println(name)
}
}
1.7、可空类型的扩展
为可空类型定义扩展函数是一种更强大的处理 null 值的方式,可以允许接收者为 null 的调用,并在该函数中处理 null ,而不是在确保变量不为 null 之后再调用它的方法
例如,如下方法可以被正常调用而不会发生空指针异常
val name: String? = null
println(name.isNullOrEmpty()) //true
isNullOrEmpty() 的方法签名如下所示,可以看到这是为可空类型 CharSequence? 定义的扩展函数,方法中已经处理了方法调用者为 null 的情况
@kotlin.internal.InlineOnly
public inline fun CharSequence?.isNullOrEmpty(): Boolean {
contract {
returns(false) implies (this@isNullOrEmpty != null)
}
return this == null || this.length == 0
}