Kotlin学习笔记(四)--空指针检查


前言:
空指针异常是Android系统上崩溃率非常非常高的异常类型,主要是因为空指针是一种不受编程语言检查的运行时异常,只能由程序员主动通过逻辑判断避免,但即使是最出色的程序员,也不可能将所有潜在的空指针异常全部考虑到。然而,kotlin却非常科学地解决了这个问题。它利用编译时判空检查机制几乎杜绝了空指针异常。

1. 可空类型系统

下面我们先来看一段代码:

fun doStudy(study:Study){
	study.readBookd()
	study.doHomework()
}

上述代码会有空指针风险吗?答案是没有。因为kotlin默认所有的参数和变量都不可为空,如果你尝试向doStudy()方法传入一个null参数,则会直接编译报错,因为Kotlin将空指针异常检查提前到了编译期。
那如果我们的业务逻辑就是需要某个变量或者参数为空怎么办呢?kotlin为我们提供了另一套可空的系统类型,只不过在使用这种类型时,我们需要在编译期就将所有潜在的空指针异常都处理掉,否则无法通过编译。
何为可为空的系统类型?就是在类名后面加上一个问号。比如String表示不可为空的字符串,而String?就表示可为空的字符串。现在回到刚才的例子:

fun main(){
	doStudy(null)
}

fun doStudy(study:Study?){
	study.readBookd()
	study.doHomework()
}

这时候调用doStudy(null)就不会提示错误了,但你会发现doStudy()中调用readBookd()和doHomework()时“.”下面出现了红色下划线错误提示,原因你应该也猜到了,我们将原来的不可空类型参数改为了可空类型参数,若传入null就会出现空指针,这种情况kotlin是不允许编译通过的。有人说加个判空处理就可以了:

fun doStudy(study:Study?){
	if(study!=null){
		study.readBookd()
		study.doHomework()
	}
}

确实可以,但如果每个地方都使用if判断语句,会让代码变得啰嗦,为此kotlin提供了一系列的辅助工具,使开发者可以更轻松地进行判空处理。

2. 判空辅助工具

2.1 ?.操作符

?.这个操作符跟在对象后面,意思是,当对象不为空时调用操作符后面的代码,如果对象为空则什么也不做。用这个操作符改造一下之前的代码:

fun doStudy(study:Study?){
	study?.readBookd()
	study?.doHomework()
}

去除了if判空语句,简化代码同时也规避了空指针风险。

2.2 ?:操作符

这个操作符左右两边都接收一个表达式,如果左边的表达式结果不为空就返回左边的结果,否则返回右边的表达式结果。例如:

//1)传统写法
fun getTextLength(text:String?){
	if(text!=null){
		return text.length
	}
	return 0
}

//2)使用操作符之后的简化写法
fun getTextLength(text:String?)=text?.length ?:0

两种方式一对比,简便性高下立判。

2.3 非空断言工具 !!

不过有时候kotlin的空指针检查机制也并非总是那么智能,我们来看一段代码:

var content:String?="hello"
fun main(){
	if(content!=null){
		printUpperCase(content)
	}
	
}

fun printUpperCase(){
	val upperCase=content.toUpperCase()
	print(upperCase)
}

从逻辑上看,这段代码并没有什么问题,但编译却无法通过,因为printUpperCase()函数并不知道外部已经对content变量进行了判空,所以在调用printUpperCase()的时候,认为存在空指针风险,所以无法编译通过。
这种情况下,如果我们想要通过编译,可以使用非空断言工具,使用方法是在对象的后面加上!!,意在告诉kotlin,我非常确信这里的对象不会为空,如果出现为空,你可以直接抛出空指针异常。

fun printUpperCase(){
	val upperCase=content!!.toUpperCase()
	print(upperCase)
}
2.4 let函数

let函数属于kotlin中的标准函数,它提供了函数式API的编程接口,并将原始调用对象作为参数传递到Lambda表达式中。let函数配合?.操作符可以在空指针检查的时候起到很大的作用。让我们通过let函数以及Lambda简化一下前面doStudy的例子:

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

是不是又再一次简化了原来的代码。另外,let函数可以处理全局变量的判空问题

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章