null
在 Java 中我们司空见惯的空指针异常 NullPointException,带给了我们很多麻烦。Kotlin 作为更强大的语言,势必会基于以往的语言涉及经验进行改良。Kotlin 更多地把运行时可能出现的 null 问题,以编译时错误的方式,提前在编译期强迫我们重视起来,而不是等到运行时报错。防范于未然,提高了我们程序的健壮性。
可空性
对于 null 值问题,Kotlin 反其道而行之,除非另有规定,变量不可能为 null 值,这样以来,运行时崩溃从根源上得到解决。如下代码
可以发现,上述代码在编译器里就直接报错了,肯定是无法通过编译的。所以, 在 kotlin 中不能给一个变量直接赋空值,除非额外的声明。
null 类型在 kotlin 里仍然存在。只是为了避免 NullPointerException,Kotlin 的做法是不让我们给非空类型变量赋 null 值,但是 null 在 kotlin 中依然存在。
可空类型
特殊声明一个可空的类型。在声明变量时加上一个 "String?",特殊声明此变量,表明它是一个可空类型。
null 安全
Kotlin 区分可空类型和非可空类型,所以,你要一个可空类型变量运行,而它又可能不存在,对于这种潜在危险,编译器时刻警惕着。为了应对这种风险,Kotlin 不允许你在可空类型值上调用函数,除非你主动接手安全管理。
a) 安全调用操作符
示例:
解释说明:str2 我们声明它是一个可空类型变量,那么编译器就不允许它调用函数。这里我们通过安全调用操作符 “?.” 来手动声明。它的作用就是,当 str2 为空时,就直接跳过调用此函数,所以执行该行代码时就不会报空指针异常。capitalize() 函数把该字符串的首字母变成大写。
如果我们没有使用 “?.” 那么在编译器就会报错。
b) 使用带 let 的安全调用
安全调用允许在可空类型上调用函数,但是如果还想做点额外的事,比如创建新值,或判断不为 null 就调用其它函数,怎么办?可以使用带 let 函数的安全调用操作符。你可以在任何类型上调用 let 函数,它的主要作用是让你在指定的作用域内定义一个或多个变量。
示例:
fun main() {
var str = "butterfly"
str?.let {
if (it.isNotBlank()){ // str 不为空,则调用capitalize() 函数
it.capitalize()
}else{
// 为空,则 赋值
"butterfly"
}
}
}
解释说明:str 为空类型变量,在它上面执行 let 函数,就可以做更多的操作,比如这里的判断你操作。let 函数返回的是匿名函数最后一行代码执行的结果。这里的 it 指的就是 str。相当于把 str 作为 let 函数的参数传入到了 let 函数里。
c) 非空断言操作符
!!. 又称感叹号操作符,当变量值为 null 时,它会去执行我们的函数,但是它会抛出 NullPointException 异常。
fun main() {
var str : String? = "hllxy"
str = null
println(str2!!.capitalize())
}
执行上述代码,结果如下
d) 使用 if 判断 null 值情况
示例:
if(str != null){
str = str.capitalize()
}else{
println("str 为 null.")
}
但是相比之下安全调用操作符用起来更灵活,代码也更简洁,我们可以用安全操作符进行多个函数的链式调用。
示例:
str = str?.capitalize()?.plus(" is great")
e)使用空合并操作符
空合并操作符相当于 java 里简略版的三元表达式。?: 操作符的意思是,如果左边的结果为 null,就使用右边的结果值。
示例:
val strWithSage = str ?: "butterfly"
空合并操作符也可以和 let 函数一起使用来代替 if/else 语句。
示例:
str = str?.let { it.capitalize() } ?: "butterfly"
异常
kotlin 里捕获异常也是通过 try...cache 来完成。
示例:
var number : Int? = null
try {
number!!.plus(1)
}catch (e : Exception){
println(e)
}
这里我们通过 非空断言操作符 !!. 来抛出异常,然后由 cache 捕获。
自定义异常
示例:
import java.lang.Exception
fun main() {
var number : Int? = null
try {
//在 !!. 之前用自定义的异常 来 处理异常
checkOperation(number)
number!!.plus(1)
}catch (e : Exception){
println(e)
}
}
// 抛出异常
fun checkOperation(number : Int?){
// 使用 空合并操作符
number ?: throw UnskilledException()
}
// 自定义异常
class UnskilledException() : IllegalArgumentException("操作不当")
先决条件函数
Kotlin 标准库提供了一些便利函数,使用这些内置函数,你可以抛出带自定义信息的异常,这些便利函数叫做先决条件函数,你可以用它定义先决条件,条件必须满足,目标代码才能执行。
示例:修改上面自定义异常的 checkOperation() 函数。
// 抛出异常
fun checkOperation(number : Int?){
// 使用 空合并操作符
//number ?: throw UnskilledException()
checkNotNull(number, { "Something is not good!" })
}
其中,checkNotNull 就是先决条件。如果 number 为空,就抛出异常。异常的内容为 {} 里的内容。