null安全与异常处理
1、null
- 在Java中我们司空见惯的空指针异常NullPointerException,带给了我们很多麻烦。Kotlin作为更强大的语言,势必会基于以往的语言设计经验对其进行改良。Kotlin更多的把运行时可能会出现的null问题,以编译时错误的方式,提前在编译期强迫我们重视起来,而不是等到运行时报错,防范于未然,提高了我们程序的健壮性。
2、可空性
- 对于null值问题,Kotlin反其道而行之,除非另有规定,变量不可为null值,这样一来,运行时崩溃从根源上得到解决。
3、Kotlin的null类型
- 为了避免NullPointerException,Kotlin的做法是不让我们给非空类型变量赋null值,但null在Kotlin中依然存在
fun main() {
//?可空字符串类型
var str: String? = "asdf"
str = null
}
4、null安全
- Kotlin区分可空类型和非可空类型,所以,你要一个可空类型变量运行,而它又可能不存在,对于这种潜在危险,编译器时刻警惕着。为了应对这种风险,Kotlin不允许你在可空类型值上调用函数,除非你主动接手安全管理。
4.1选项一:安全调用操作符
- 这次Kotlin不报错了,编译器看到有安全调用操作符,所以它知道如何检查null值。如果遇到null值,它就跳过函数调用,而不是返回null。
fun main() {
var str: String? = "asdf"
str = null
//?安全检查,当为null时,就不调用这个函数
str?.capitalize()
}
4.2使用带let的安全调用
- 安全调用允许在可空类型上调用函数,但是如果还想做点额外的事,比如创建新值,或判断不为null就调用其他函数,怎么办?可以使用带let的安全调用操作符。你可以在任何类型上调用let函数,它的主要作用是让你在指定的作用域内定义一个或多个变量。
- let函数返回lambda表达式最后一行的结果
fun main() {
var str: String? = "asdf"
str = ""
//let不论str是否为null都会进行let,在let中可以进行各种判断
str = str?.let {
if (it.isNotBlank()) {
it.capitalize()
} else {
"Butterfly"
}
}
println(str)
}
4.3选项二:使用非空断言操作符
!!
又称叹号操作符,当变量值为null时,会抛出KotlinNullPointerException。
fun main() {
var str: String? = null
str = str!!.capitalize()
println(str)
}
4.4选项三:使用if判断null值的情况
- 我们也可以使用if判断,但是相比之下安全调用操作符用起来更灵活,代码也更简洁,我们可以用安全操作符进行多个函数的链式调用。
fun main() {
var str: String? = null
if (str != null) {
str = str.capitalize()
} else {
println("为null")
}
}
4.5使用空合并操作符
- ?:操作符的意思是,如果左边的求值结果为nul,就使用右边的结果值
fun main() {
var str = "asdf"
str = str ?: "butterfly"
}
- 空合并操作符也可以和let函数一起使用来替代if/else语句
fun main() {
var str: String? = null
str = str?.let { it.capitalize() } ?: "butterfly"
}
5.异常
- 抛出异常
- 自定义异常
- 异常处理
fun main() {
val number: Int? = null
try {
number!!.plus(1)
} catch (e: Exception) {
println(e)
}
}
fun main() {
val number: Int? = null
try {
checkOpt(number)
number!!.plus(1)
} catch (e: Exception) {
println(e)
}
}
fun checkOpt(number: Int?) {
number ?: throw UnskilledException()
}
//自定义异常
class UnskilledException() : IllegalArgumentException("操作不当")
6.先决条件函数
-
Kotlin标准库提供了一些遍历函数,使用这些内置函数,你可以抛出带自定义信息的异常,这些便利函数叫做先决条件函数,你可以用它定义先决条件,条件必须满足,目标代码才能执行。
- checkNotNull:如果参数为null,则抛出
IllegalStateException
异常,否则返回非null值 - require:如果参数为false,则抛出
IllegalArgumentException
异常 - requireNotNull:如果参数为null,则抛出
IllegalStateException
异常,否则返回非null值 - error:如果参数为null,则抛出
IllegalStateException
异常并输出错误信息,否则返回非null值 - assert:如果参数为false,则抛出
AssertError
异常,并打上断言编译器标记
- checkNotNull:如果参数为null,则抛出
fun main() {
val number: Int? = null
try {
checkOpt(number)
number!!.plus(1)
} catch (e: Exception) {
println(e)
}
}
fun checkOpt(number: Int?) {
checkNotNull(number, { "Something is not good" })
}