目录
引言
某国外机构做过一个统计,Android系统上崩溃率最高的异常类型就是空指针异常(NullPointerException),空指针是一种不受编程语言检查的运行时异常,只能由程序员主动通过逻辑判断来避免。
Kotlin却非常科学地解决了这个问题,它利用编译时判空检查的机制几乎杜绝了空指针异常。虽然编译时判空检查的机制有时候会导致代码变得比较难写,但是Kotlin提供了 一系列的辅助工具,让我们能轻松地处理各种判空情况(和Dart挺相似的)。
正文
空指针检查机制
fun doStudy(study: Study) {
study.readBooks()
study.doHomework()
}
Kotlin写法虽然看上去似乎和Java没有什么区别,但实际上它是没有空指针风险的,因为Kotlin默认所有的参数和变量都不可为空。所以这里传入的Study参数也一定不会为空,我们可以放心地调用它的任何函数。
假设在调用doStudy方法传null会发生什么呢?
编译报错。Kotlin将空指针异常的检查提前到了编译时期,修正之后才能成功运行,这样就可以保证程序在运行时期不会出现空指针异常了。
可为空的类型系统
那如果业务逻辑就是需要某个参数或者变量为空呢?Kotlin提供了另外一套可为空的类型系统,只不过在使用可为空的类型系统时,我们需要在编译时期就将所有潜在的空指针异常都处理掉,否则代码将无法编译通过。
在类名的后面加上一个问号。比如,Int表示不可为空的整型,而Int?就表示可为空的整型;String表示不可为空的字符串,而String?就表示可为空的字符串。
图1.1
由于将参数改成了可为空的Study?类型,此时调用参数的readBooks()和doHomework()方法都可能造成空指针异常,因此Kotlin在这种情况下不允许编译通过。
怎么解决呢?使用if判空固然可以,如果每处检查代码都使用if判断语句,则会让代码变得比较啰嗦,而且if判断语句还处理不了全局变量的判空问题。
判空辅助工具
?.操作符
当对象不为空时正常调用相应的方法,当对象为空时则什么都不做。
?:操作符
这个操作符的左右两边都接收一个表达式, 如果左边表达式的结果不为空就返回左边表达式的结果,否则就返回右边表达式的结果。
val c = if (a != null) a else b
|
|
v
val c = a ?: b
//实例
fun getTextLength(text: String?) = text?.length ?: 0
非空断言工具
!!操作符
Kotlin的空指针检查机制也并非总是那么智能,有的时候我们可能从逻辑上已经将空指针异常处理了,但是Kotlin的编译器并不知道,这个时候它还是会编译失败。
这段代码一定是无法运行的。因为printUpperCase()函数并不知道外部已经对content变量进行了非空检查,在调用toUpperCase()方法时,还认为这里存在空指针风险,从而无法编译通过。
如果我们想要强行通过编译,可以使用非空断言工具,写法是在对象的后面加上!!
fun printUpperCase() {
val upperCase = content!!.toUpperCase()
println(upperCase)
}
这是一种有风险的写法,意在告诉Kotlin,我非常确信这里的对象不会为空,所以不用你来帮我做空指针检查了,如果出现问题,你可以直接抛出空指针异常,后果由我自己承担。
与众不同的辅助工具—let
let既不是操作符,也不是什么关键字,而是一个函数。这个函数提供了函数式API的编程接口,并将原始调用对象作为参数传递到Lambda表达式中。
实例:
obj.let { obj2 ->
// 编写具体的业务逻辑
}
这里调用了obj对象的let函数,然后Lambda表达式中的代码就会立即执行,并且这个obj对象本身还会作为参数传递到Lambda表达式中。不过,为了防止变量重名,这里将参数名改成了obj2,但实际上它们是同一个对象,这就是let函数的作用。
let函数和空指针检查有什么关系呢?其实let函数的特性配合?.操作符可以在空指针检查的时候起到很大的作用。
以图1.1代码举例,虽然使用了?.操作符简化了if的写法,但是准确翻译为if的写法,实际却进行了两次判断,表达方式也有点啰嗦。
使用let函数结合?.操作符:
fun doStudy(study: Study?) {
study?.let {
it.readBook()
it.doHomework()
}
}
?.操作符表示对象为空时什么都不做,对象不为空时就调用let函数,而let函数会将study对象本身作为参数传递到Lambda表达式中,此时的study对象肯定不为空了,我们就能放心地调用它的任意方法了(Lambda写法简化了,省去了传入let函数体中的参数声明,详见)。
let函数是可以处理全局变量判空的问题。下图中使用if判断任然报错的原因:全局变量的值随时都有可能被其他线程所修改,即使做了判空,仍无法保证没有空指针风险,而这也体现了let函数的优势。
标签:函数,检查,Kotlin,编译,let,操作符,指针 From: https://blog.csdn.net/canada_numbfish/article/details/136809058