Kotlin学习(四):空指针检查

4.1 可空类型系统

Kotlin在代码编译阶段会检查所有参数和变量是否为非空,若有空值则报错

在这里插入图片描述

当我们的业务逻辑需要某个参数或变量为空时,Kotlin为我们提供了一套可为空的类型系统

但在使用这套系统时,我们需要在编译前就将所有潜在的空指针异常处理掉,否则代码将无法编译通过

可为空的类型系统,就是在原来类型名后加上?,如Int表示不可为空的整型,而Int?则表示可为空的整型

对于上图,在类型名Student后加上问号,改为Student?,就能传入null参数了

在这里插入图片描述

但又出现了新的红线,因为此时可传入null参数,此时调用方法都可能造成空指针异常,因此Kotlin不允许在可传入null参数的方法中,不做非空判断的情况下直接调用类方法

fun doStudy(stu:Student?){
    if(stu != null){//对对象stu作非空检查,这样就能避免空指针异常并通过编译了
        stu.readBooks()
        stu.doHomework()
    }
}

为了在编译时期处理掉所有空指针异常,通常需要编写很多额外的检查代码。如果每处检查代码都使用if条件语句,会让代码比较啰嗦,而且if判断语句处理不了全局变量的判空问题。为此,Kotlin还提供了一系列判空处理的辅助工具。

4.2 判空辅助工具

4.2.1 ?.操作符

?.操作符就是当对象不为空时,正常调用相应方法;当对象为空时什么也不做,如:

if(a != null){
	a.doSomething()
}
//对以上判空方法,用?.操作符可简化为:
a?.doSomething()

对上节的doStudy()函数,可简化为:

fun doStudy(stu:Student?){
    stu?.doHomework()
    stu?.readBook()
}

4.2.2 ?:操作符

?:操作符左右两边都接受一个表达式,若左边表达式结果不为空就返回左边表达式结果,否则返回右边表达式的结果

val c = if(a != null){a}else{b}
//对以上操作,用?:操作符可写为:
val c = a ?: b//使用方法类似于C语言的问号表达式

对于一个获取字符串长度的函数:

fun getStringLength(str:String?):Int{
    if(str!=null){//使用if判断语句判空
        return str.length
    }
    return 0
}

当我们用?.操作符和?:操作符后可写为:

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

4.2.3 非空断言工具

有时Kotlin代码从逻辑上已经完成了空指针异常处理,但编译器可能还是会编程失败:

var content:String?="Hello"
fun printUpperCase(){
    val upperCase= content.toUpperCase()//代码无法编译通过,因为编译器认为content可能为空
    println(upperCase)
}
fun main(){
    if(content!=null){//但实际上我们已经对content作了非空判断
        printUpperCase()
    }
}

如果我们想要强行通过编译,可以使用非空断言工具!!,即在对象后加上!!

fun printUpperCase(){
    val upperCase= content!!.toUpperCase()//此时编译可以通过,我们告诉编译器此处对象不为空
    println(upperCase)//但这样做是有风险的,因为我们跳过了编译器在此处的非空检查
}

4.2.4 let函数

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

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

这里调用了obj对象的let函数,然后Lambda表达式中的代码就会立即执行,并且该对象本身会作为参数传递到Lambda表达式中

为了防止重名,我们将参数名改为obj2,但实际上它们是同一个对象

//对于之前的doStudy()函数:
fun doStudy(stu:Student?){
    stu?.doHomework()
    stu?.readBook()
}
//将?.操作符翻译成if:
fun doStudy(stu:Student?){
    if(stu != null){
        stu.doHomework()
    }
    if(stu != null){
        stu.readBook()
    }    
}//这里可以看出,?.操作符在这里的使用比较啰嗦
//如果使用?.操作符和let函数结合:
fun doStudy(stu:Student?){
    study?.let{study->//?.操作符使对象为空时不作任何操作
        study.readBook()//若不为空,则let函数将stu对象本身作为参数传递到Lambda表达式中
        study.doHomework()//此时对象一定不为空,可以执行方法
    }
}
//当Lambda表达式的参数列表中只有一个参数时,可以用it关键字指代参数名
fun doStudy(stu:Student?){
    study?.let{
        it.readBook()
        it.doHomework()
    }
}

4.2.5 特别注意

if判断语句无法对全局变量判断非空,如图

在这里插入图片描述

因为全局变量的值随时可能被其他线程修改,即使判空处理也无法保证if语句中的study变量没有空指针风险

而let函数可以判断全局指针是否为空

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

BinBalll

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值