Kotlin中的空指针检查

在任何编程语言中,空指针异常高居各类崩溃排行榜榜首!!!主要是因为空指针是一种不受编程语言检查的运行时异常,只能由程序员主动通过逻辑判断来避免,

Kotlin科学的解决了这个问题,在编译时的判空检查机制几乎杜绝了空指针异常。

1.1 可空类型系统

声明:下面代码的Study 为自行创建的一个接口Study,代码如下:

interface Study {
    fun readBooks()
    fun doHomeWork(){
        println("do homework default implementation!")
    }
}

首先,Kotlin是默认所有的参数和变量都不为空!!!在编译时,不能直接在函数中传入一个null参数,会在编译时直接报错。例如:

fun main(){
    doStudy(null)
}

fun doStudy(study: Study) {
    study.doHomeWork()
    study.readBooks()
}

doStudy在编译时会报错,不允许传入null空指针。

但是,所有的变量都不可为空,会导致当我们的业务逻辑需要某个参数或变量为空时,不能实现代码,所以Kotlin提供了可空类型系统。

可空类型系统:在类名后加一个?  表示,该参数或变量可为空。刚才的例子修改为如下:

fun main(){
    doStudy(null)
}

fun doStudy(study: Study?) {
    study.doHomeWork()
    study.readBooks()
}

但是,这时会出现一个情况,doStudy()中的函数会报错,因为当传入空指针时,接口中的方法可能会造成空指针异常,不可实现。因此需要进行判空处理,即:

fun doStudy(study: Study?) {
    if (study != null) {
        study.doHomeWork()
        study.readBooks()
    }
}

但是,为了在编译时处理掉所有的空指针异常,需要很多行检查代码,每次检查都需要一个if判空语句,代码会变得繁琐,也有可能会遗忘掉某些判空处理。Kotlin提供了一系列的辅助工具帮助进行判空处理。

1.2 判空辅助工具

首先、学习使用 ?.  :当对象不为空时,正常调用相应方法。

例如下列代码:

if (a != null) {
    a.doSomething()
}

可以改变为:

a?.doSomething()

根据 ?. 可以对doStudy函数进行优化,进一步简化了代码,如下:

fun doStudy(study: Study?) {
    study?.doHomeWork()
    study?.readBooks()
}

另外学习一个  ?:  操作符,在操作符左右都接收一个表达式,如果左边表达式的结果不为空则返回左边结果,否则返回右边表达式结果。

正常代码如下:

val c = if(a != null){
    a
}else{
    b
}

使用  ?: 操作符可简化为:

val c = a ?: b

一个具体的例子,获取某个字符串的长度,正常代码可以写为;

fun getStringLength(text: String?) :Int {
    if (text != null) {
        return text.length
    }
    return 0
}

根据判空操作符优化,以及语法糖,可以更改为:

fun getStringLength(text: String?) = text?.length ?: 0

在该函数中,text可能为空,因此输入参数时需要判空,执行函数体时,由于输入的参数text会出现为空的情况,因此需要使用 ?. 来判断是否为空。然后借助 ?: 来实现当text为空时返回0的情况。

不过有时,我们已经做了空指针异常处理,但是Kotlin编译器并不知道,还是会编译失败,代码如下:

var content: String? = "hello"

fun main() {
    if (content != null) {
        printUpperCase()
    }
}

fun printUpperCase() {
    val upperCase = content.uppercase()
    println(upperCase)
}

全局变量content可能为空,在主函数中,先对content进行了判空处理,当不为空时,执行某函数。到了函数内部,调用uppercase()函数,Kotlin会认为这里存在空指针风险,编译器不知道我们在主函数内进行了判空,因此编译无法通过。如果想强行编译,可以这样写:

val upperCase = content!!.uppercase()

!! 是告诉Kotlin 我肯定这里的对象不为空,不用编译器做编译检查了,出现的后果自行承担。但是是一个非常危险的操作。这有更好的实现方式,使用一个不同的工具-------let函数

let函数提供了函数式API编程接口,并将原始调用对象作为参数传递到Lambda表达式中。

obj.let { obj2 ->
    //编写具体业务逻辑
}

调用let函数,会将对象本身作为参数传递到Lambda表达式中。

根据let函数,上述printUpperCase()可以写成:

fun printUpperCase() {
    println(content?.let { it.uppercase() })
}

还有一点,使用let函数能够处理全局变量的判空问题,而if判断语句无法做到这一点,比如我们将doStudy()中的函数参数变为全局变量,使用if语句,会报错,使用let函数能够正常运行。如下:

因为全局变量的值可能被其他线程修改,可以将var,改为val,也可以使用let函数来对stu进行判空:

 这样就不用考虑其他线程修改的问题了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值