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函數可以處理全局變量的判空問題

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