本系列内容均来自《Kotlin从小白到大牛》一书,感谢作者关东升老师。
Kotlin语言与Swift语言类似, 默认情况下所有的数据类型都是非空类型( NonNull)
, 声明的变量都是不能接收空值( null) 的。 这一点与Java和Objective-C等语言有很大的不同。
1 可空类型概念
Kotlin的非空类型设计能够有些防止空指针异常( NullPointerException) , 空指针异常引起的原因是试图调用一个空对象的函数或属性, 则抛出空指针异常。 在Kotlin中可以将一个对象的声明为非空类型, 那么它就永远不会接收空值, 否则会发生编译错误。
示例代码如下:
var n: Int = 10
n = null //发生编译错误
上述代码n = null会发生编译错误, 因为Int是非空类型, 它所声明的变量n不能接收空值。 但有些场景确实没有数据, 例如查询数据库记录时, 没有查询出符合条件的数据是很正常的事情。 为此, Kotlin为每一种非空类型提供对应的可空类型( Nullable) , 就是在非空类型后面加上问号( ?) 表示可空类型
。 修改上面示例代码:
var n: Int? = 10
n = null //可以接收空值( null)
Int?是可空类型, 它所声明的变量n可以接收空值。 可空类型在具体使用时会有一些限制:
- 不能直接调用可空类型对象的函数或属性。
- 不能把可空类型数据赋值给非空类型变量。
- 不能把可空类型数据传递给非空类型参数的函数。
为了“突破”这些限制, Kotlin提供了如下运算符:
- 安全调用运算符: ?.
- 安全转换运算符: as?
- Elvis运算符: ?:
- 非空断言: !!
此外, 还一个let函数帮助处理可空类型数据,本文重点介绍安全调用运算符( ?.) 、Elvis运算符( ?:) 和非空断言( !!) 。
2 使用安全调用运算符( ?.)
可空类型变量使用安全调用运算符( ?.) 可以调用非空类型的函数或属性。 安全调用运算符( ?.) 会判断可空类型变量是否为空, 如果是则不会调用函数或属性, 直接返回空值;否则返回调用结果
。
示例代码如下:
//声明除法运算函数
fun divide(n1: Int, n2: Int): Double? {
130
if (n2 == 0) {//判断分母是否为0
return null
}
return n1.toDouble() / n2
}
fun main(args: Array<String>) {
val divNumber1 = divide(100, 0) ①
val result1 = divNumber1?.plus(100)//divNumber1+100, 结果null ②
println(result1)
val divNumber2 = divide(100, 10) ③
val result2 = divNumber2?.plus(100)//divNumber2+100, 结果110.0 ④
println(result2)
}
上述代码自定义了divide函数进行除法运算, 当参数n2为0的情况下, 函数返回空值, 所以函数返回类型必须是Double的可空类型, 即Double?。
代码第①行和第③行都调用divide函数, 返回值divNumber1和divNumber2都是可空类型, 不能直接调用plus函数, 需要使用“?.”调用plus函数。 事实上由于divNumber1为空值, 代码第②行并没有调用plus函数, 而直接返回空值。 而代码第④行是调用了plus函数进行计算返回结果。
3 非空断言运算符( !!)
可空类型变量可以使用非空断言运算符( !!) 调用非空类型的函数或属性。 非空断言运算符( !!) 顾名思义就是断言可空类型变量不会为空, 调用过程是存在风险的,如果可空类型变量真的为空, 则会抛出空指针异常; 如果非则可以正常调用函数或属性
。
将上面的代码修改如下:
fun main(args: Array<String>) {
val divNumber1 = divide(100, 10)
val result1 = divNumber1!!.plus(100)//divNumber1+100, 结果110.0 ①
println(result1)
val divNumber2 = divide(100, 0)
val result2 = divNumber2!!.plus(100)//divNumber2+100, 结果抛出异常 ②
println(result2)
}
运行结果:
110.0
Exception in thread "main" kotlin.KotlinNullPointerException
at com.a51work6.Ch6_4_4Kt.main(ch6.4.4.kt:12)
上述代码第①行和第②行都调用plus函数, 代码第①行可以正常调用, 而代码第②行, 由于divNumber2是空值, 非空断言调用会发生异常。
4 使用Elvis运算符( ?:)
有的时候在可空类型表达式中, 当表达式为空值时, 并不希望返回默认的空值, 而是其他数值
。 此时可以使用Elvis运算符( ?:) , 也称为空值合并运算符, Elvis运算符有两个操作数, 假设有表达式: A ?: B, 如果A不为空值则结果为A; 否则结果为B。
Elvis运算符经常与安全调用运算符结合使用, 重写上一节示例代码如下
fun main(args: Array<String>) {
val divNumber1 = divide(100, 0)
val result1 = divNumber1?.plus(100) ?: 0//divNumber1+100, 结果0 ①
println(result1)
val divNumber2 = divide(100, 10)
val result2 = divNumber2?.plus(100) ?: 0//divNumber2+100, 结果110.0 ②
println(result2)
}
代码第①行和第②行都是用了Elvis运算符, divNumber1?.plus(100)表达式为空值,则返回0。 divNumber2?.plus(100)表达式不为空值, 则返回110.0。