Kotlin空安全与异常

本文最后更新于:2022年12月18日 中午

null安全与异常

Koltin受不了Java里常见的空指针异常,所以做出了改良。Kotlin更多地把运行时可能会出现的null问题,以编译错误的方式,提前在代码编译期强迫我们重视起来。

可空性

对于null值问题,Kotlin反其道而行之,除非另有规定,变量不可为null值,这样一来,运行时崩溃从根源上得到解决。

// 编译无法通过
fun main() {
    var str = "Hi,Yorick!"
    str = null
}

如果我偏要赋null值呢?

fun main() {
    var str:String? = "Hi,Yorick!"
    str = null
}

null安全

Kotlin区分可空类型和非可空类型,所以,你要一个可空类型变量运行,而它又可能不存在,对于这种潜在危险,编译器时刻警惕着。为了应对这种风险,Kotlin不允许你在可空类型值上调用函数,除非你主动接手安全管理。

安全调用操作符

直接调用空类型的方法,编译器会报错

fun main() {
    var str:String? = "Hi,Yorick!"
    str = null
    // 下面会报错
    println(str.length)
}

但是使用安全调用操作符?.,就可有避免,?.的作用为调用者非空才执行。

fun main() {
    var str:String? = "Hi,Yorick!"
    str = null
    println(str?.length)
}

使用带let的安全调用

安全调用允许在可空类型上调用函数,但是如果还想做点额外的事,比如创建新值,或判断不为null就调用其他函数,怎么办?

可以使用带let函数的安全调用操作符。你可以在任何类型上调用let函数,它的主要作用是让你在指定的作用域内定义一个或多个变量。

fun main() {
    var str: String? = "hi,Yorick!"
    str = str?.let {
        // 非空白的字符串
        if (it.isNotBlank()) {
            it.capitalize()
        } else {
            "Hi,Yorick!"
        }
    }
    println(str)
}
// 输出
// Hi,Yorick!

使用!!.操作符

!!.非空断言操作符,强制执行方法,会抛出空指针异常。

fun main() {
    var str: String? = "hi,Yorick!"
    str = null
    println(str!!.capitalize())
}

使用if判断null值

这里就是传统Java的使用if判断null值,可以比较一下,Kotlin用一个?就解决了。

fun main() {
    var str: String? = "hi,Yorick!"
    if (str != null) {
        str = str.capitalize()
    } else {
        println("为空")
    }
    println(str)
}

我们可以使用if判断,但是相比之下安全调用操作符用起来更灵活,代码也更简洁,我们可以用安全操作符进行多个函数的链式调用。

fun main() {
    var str: String? = "hi,Yorick."
    str = str?.capitalize().plus("Have a nice day!")
    println(str)
}
// 输出
// Hi,Yorick.Have a nice day!

如果第一个?.方法的调用者为空,那么后面的方法都不会被调用。

使用空合并表达式

?:操作符的意思是,如果左边的求值结果为null,就使用右边的结果值。

fun main() {
    var str: String? = "hi,Yorick."
    str = null
    println(str ?: "yeah!")
}
// 输出
// yeah!

空合并操作符也可以和let函数一起使用来代替if/else语句。

fun main() {
    var str: String? = "hi,Yorick."
    str = str?.let { it.capitalize() } ?: "yeah!"
    println(str)
}

异常

异常处理:

fun main() {
    val number: Int? = null
    try {
        number!!.plus(1)
    } catch (e: KotlinNullPointerException) {
        println(e)
    }
}

自定义异常:

import java.lang.IllegalArgumentException

fun main() {
    val number: Int? = null
    try {
        checkOperation(number)
        number!!.plus(1)
    } catch (e: KotlinNullPointerException) {
        println(e)
    }
}

fun checkOperation(number: Int?) {
    number ?: throw UnskilledException()
}

// 自定义异常
class UnskilledException : IllegalArgumentException("操作不当")

先决条件函数

Kotlin标准库提供了一些便利函数,使用这些内置函数,你可以抛出带自定义信息的异常,这些便利函数叫做先决条件函数,你可以用它定义先决条件,条件必须满足目标代码才能执行。

// 在上面代码的基础上修改
fun checkOperation(number: Int?) {
    checkNotNull(number) { "Something is Null!" }
}
函数 描述
checkNotNull 如果参数为null,则抛出IllegalStateException异常,否则返回非null值
require 如果参数为false,则抛出IllegalArgumentException异常
requireNotNull 如果参数为null,则抛出IllegalStateException异常,否则返回非null值
error 如果参数为null,则抛出IllegalStateException异常并输出错误信息,否则返回非null值
assert 如果参数为false,则抛出AssertError异常,并打上断言编译器标记

Kotlin空安全与异常
https://yorick-ryu.github.io/Kotlin/Kotlinnull安全与异常/
作者
Yorick
发布于
2022年9月7日
许可协议