Kotlin學習路(二):空指針

<本文學習郭神《第三行代碼》總結>

1、可空類型

Kotlin在編譯時,會有自己的判空機制,可以幾乎杜絕空指針異常
首先,寫一段代碼:

fun setDate(a: Test) {
    a.getText()
    a.getContent()
}

這段代碼沒有空指針風險,並且這段代碼無法傳入空參數。
因爲Kotlin默認所有參數不可爲空,所以這裏傳入的參數也是不可空的。如果這裏傳入一個null參數,則會提示錯誤:Null can not bea value of a non-null type Study,也就是說,Kotlin在將空指針異常的檢查提前到了編譯期。
如果開發過程中,業務邏輯需要使用null參數,則需要進行可空類型操作。
可空類型很簡單,就是在類名後加一各問好,比如:

Int 表示不可空整型
Int? 表示可空整型
String 表示不可控字符串
String? 表示可空字符串

那麼,將上述代碼修改爲

fun setDate(a: Test?) {
    a.getText()
    a.getContent()
}

就可以傳入null參數,就不會再提示錯誤。
但是,傳入了空參數,會導致方法裏面的調用方法getText()、getContent()出現空指針異常,無法通過Kotlin編譯,所以必須要進行判空處理。

fun setDate(a: Test?) {
	if (a != null) {
	    a.getText()
	    a.getContent()
	}
}

2、判空輔助工具

(1)?. 操作符
如果需要判空處理過多,則會寫很多if,導致很繁瑣,而且if也無法處理全局變量,所以這裏需要使用輔助工具。
比如,對以下代碼進行判空處理進行簡化

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

簡化成

a?.getText()

這裏?.就是判空操作符,所以上面的代碼就可以寫成

fun setDate(a: Test?) {
    	   a?.getText()
    a?.getContent()
}

這樣,通過使用?.操作符就可替換if

(2)?: 操作符
這個操作符左右兩邊都接收一個表達式,如果左邊表達式結果不爲空,則返回左邊,否則,返回右邊。

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

可以簡化成

val c = a ?: b

(3)兩種操作符共同使用
比如一段代碼

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

可以簡化成

fun getData(content : String?) = content?.length ?: 0

(4)!! 操作符
Kotlin判空機制也存在漏洞,因爲有時候從邏輯上已經判空處理,但是編譯器並不知道,還是會編譯失敗。

var content: String? = "aa"
fun main(){
    if (content != null){
        val text = content.toUpperCase()
    }
}

定義一個全局變量content,在main方法中進行判空處理,當不爲空時執行下一步操作。
但是這段代碼無法編譯運行,因爲toUpperCase()並不知道外部已經對content進行了判空處理,所以在調用時,還是認爲存在空指針風險。
在這種情況下,要想通過編譯,只能使用 !! ,進行強制通過。

var content: String? = "aa"
fun main(){
    if (content != null){
        val text = content!!.toUpperCase()
    }
}

這是一種風險寫法,因爲這是在強制進行通過編譯,如果在開發中,出現null情況,則程序會崩潰。

(5)let
let不是操作符,也不是關鍵字,而是函數。這個函數提供了函數式的API編程接口,並將調用對象作爲參數傳遞到Lambda表達式中。

obj.let { obj2 ->
//函數體
}

這裏,調用了obj對象的let函數,然後Lambda表達式中的代碼就會立即執行,並且這個obj對象本身還會作爲參數傳遞到Lambda表達式中,這裏,爲了防止重名,將obj參數名改成了obj2,實際是同一個對象。

fun setDate(a: Test?) {
    	   a?.getText()
    a?.getContent()
}

這段代碼通過?. 操作符可以正常編譯,如果將這段換成if判斷,則是:

fun setDate(a: Test?) {
	if (a != null){
	    	   		a?.getText()
	}
	if (a != null){
	    	   	a?.getContent()
	}
}

也就是說,使用?. 每次調用方法,都會進行一次if 判斷,這樣會增加邏輯運算,不簡潔。所以還可以簡化成

fun setDate(a: Test?) {
    a?.let { a1 -> 
        a1.getText()
        a1.getContent()
}

?. 操作符表示對象爲空,什麼都不做,對象不爲空就調用let函數,而let函數會將a 對象本身作爲參數傳遞到Lambda表達式中,此時a 對象肯定不爲空。
當Lambda參數只有一個時,可以直接用it關鍵字代替,所以這裏可以簡化成:

fun setDate(a: Test?) {
    a?.let { 
        it.getText()
        it.getContent()
}

此外,let函數還可以進行全局變量的判空問題處理,而使用if則不行,使用if對全局變量判空則需要使用!!進行強制編譯通過,使用let不會出現這種情況。

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