當前位置:
首頁 > 最新 > Kotlin入門(8)空值的判斷與處理

Kotlin入門(8)空值的判斷與處理

以往的開發工作之中,少不了要跟各種異常作鬥爭,常見的異常種類包括空指針異常NullPointerException、數組越界異常IndexOutOfBoundsException、類型轉換異常ClassCastException等等,其中最讓人頭痛的當數空指針異常,該異常頻繁發生卻又隱藏很深。調用一個空對象的方法,就會產生空指針異常,可是Java編碼的時候編譯器不會報錯,開發者通常也意識不到問題,只有App運行之時發生閃退,查看崩潰日誌才會恍然大悟「原來這裡得加上對象非空的判斷」。然而,饒是有經驗的開發者,尚且擺脫不了如影隨形的空指針,何況編程新手呢?問題的癥結在於,Java編譯器不會檢查空值,只能由開發者在代碼中增加「if (*** != null)」的判斷,但是業務代碼裡面的方法調用浩如繁星,倘若在每個方法調用之前都加上非空判斷,勢必大量代碼都充滿了「if (*** != null)」,這樣做的後果不僅降低了代碼的可讀性,而且給開發者帶來不少的額外工作量。

空指針只是狹義上的空值,廣義上的空值除了空指針,還包括其它開發者認可的情況。比如說String類型,字元串的長度為0時也可算是空值;如果字元串的內容全部由空格組成,某種意義上也是空值。那麼字元串的非空判斷,用Java書寫的話見下面示例代碼:

if (str!=null && str.length()>0 && str.trim().length()>0) {

......

}

可以看到以上的非空判斷語句有點冗長了,因此作為開發者,必須把會被多次調用的代碼封裝成工具類。既然大家都這麼想,Android系統的研發工程師也不例外,所以安卓的SDK已經提供了TextUtils.isEmpty(***)這個公共方法,專門用於校驗某個字元串是否為空值。Kotlin的研發人員當然不會放過這點,就像讀者在上一篇文章中看到的那樣,Kotlin通過isNullOrBlank函數進行空值校驗,下面列出Kotlin校驗字元串空值的幾個方法:

isNullOrEmpty : 為空指針或者字串長度為0時返回true,非空串與可空串均可調用。

isNullOrBlank : 為空指針或者字串長度為0或者全為空格時返回true,非空串與可空串均可調用。

isEmpty : 字串長度為0時返回true,只有非空串可調用。

isBlank : 字串長度為0或者全為空格時返回true,只有非空串可調用。

isNotEmpty : 字串長度大於0時返回true,只有非空串可調用。

isNotBlank : 字串長度大於0且不是全空格串時返回true,只有非空串可調用。

注意到上面的方法有區分非空串與可空串,這是緣於Kotlin引入了空安全的概念,每個類型的對象都分作不可為null和可以為null兩種。前面的文章中,正常聲明的對象默認都是非空(不可為null),比如下面這個聲明字元串變數的代碼

var strNotNull:String = ""

非空對象要麼在聲明時就賦值,要麼在方法調用前賦值;否則未經初始化就調用該對象的方法,Kotlin會像語法錯誤那樣提示這裡「Variable *** must be initialized」。至於可以為空的對象,可於聲明之時在類型後面加個問號,如同上一篇文章聲明可空字元串數組的代碼「val poem2Array:Array = ***」,只聲明一個可空字元串對象的代碼如下所示:

var strCanNull:String?

現在有了兩個字元串,其中strNotNull為非空串,strCanNull為可空串。按照前面幾個字元串空值校驗方法的規則,strNotNull允許調用全部六個方法,但strCanNull只允許調用isNullOrEmpty和isNullOrBlank兩個方法。因為strCanNull可能為空指針,若去調用一個空指針對象的length方法,毫無疑問會扔出空指針異常,所以Kotlin對可空串增加了編譯檢查,一旦發現某個可空串調用isEmpty/isBlank/isNotEmpty/isNotBlank,立刻提示此處語法錯誤「Only *** calls are allowed on a nullable receiver of type String」。

可是上述的幾個方法局限於判斷字元串是否為空串,如果要求獲得字元串的長度,或者調用其它對象類型的方法,仍然要判斷空指針。以獲取字元串長度為例,下面聲明了三個字元串對象,其中strA為非空串,strB和strC都是可空串,不過strB為空而strC實際有值,字元串對象的聲明代碼如下:

val strA:String = "非空"

val strB:String? = null

val strC:String? = "可空串"

對於strA,因為它是非空串,所以可直接獲取length長度屬性。對於strB和strC,必須進行非空判斷,否則編譯器會提示該行代碼存在錯誤。具體的長度獲取代碼如下所示:

var length:Int = 0

btn_length_a.setOnClickListener { length=strA.length; tv_check_result.text="字元串A的長度為$length" }

btn_length_b.setOnClickListener {

//length=strB.length //這種寫法是不行的,因為strB可能為空,會扔出空指針異常

length = if (strB!=null) strB.length else -1

tv_check_result.text="字元串B的長度為$length"

}

btn_length_c.setOnClickListener {

//即使strC實際有值,也必須做非空判斷,誰叫它號稱可空呢?編譯器寧可錯殺一千、不可放過一個

length = if (strC!=null) strC.length else -1

tv_check_result.text = "字元串C的長度為$length"

}

以上的if/else雖然已經完成非空判斷的功能,可是Kotlin仍舊嫌它太啰嗦,中國人把繁體字簡化為簡體字,外國人也想辦法簡化編程語言,中外人士果然所見略同。原本直接獲取可空串的length屬性會扔出空指針異常,那就加個標記,遇到空指針別扔異常,直接返回空指針就好了,至少避免了處理異常的麻煩事。具體的標記代碼如下:

var length_null:Int?

btn_question_dot.setOnClickListener {

//?.表示對象為空時就直接返回null,所以返回值的變數必須被聲明為可空類型

length_null = strB?.length

tv_check_result.text = "使用?.得到字元串B的長度為$length_null"

}

從代碼中可以看出,這個多出來的標記是個問號,語句「strB?.length」等價於「length_null = if (strB!=null) strB.length else null」。但是,該語句意味著返回值仍然可能為空,如果不想在界面上展示「null」,還得另外判斷length_null是否為空;也就是說,這個做法並未實現與原代碼完全一致的功能。

沒有完成任務,Kotlin當然不會罷休,所以它又引入了一個運算符「?:」,學名叫做「Elvis 操作符」,叫起來有點拗口,讀者可以把它當作是Java三元運算符「變數名=條件語句?取值A:取值B」的縮寫。引入「?:」的實現代碼如下所示:

btn_question_colon.setOnClickListener {

//?:表示為空時就返回右邊的值,即(x!=null)?x.**:y

length = strB?.length?: -1

tv_check_result.text = "使用?:得到字元串B的長度為$length"

}

這樣總該完事了吧?然而執拗的Kotlin攻城獅覺得還是啰嗦,因為經常上一行代碼就對strB賦值了,所以此時可以百分百保證strB非空,那又何必浪費口舌呢?於是Kotlin另外引入了運算符「!!」,表示甭管那麼多了,前方沒有地雷,弟兄們趕緊上!下面是「!!」的運用代碼例子:

btn_exclamation_two.setOnClickListener {

strB = "排雷完畢"

length = strB!!.length

tv_check_result.text = "使用?:得到字元串B的長度為$length"

}

既然運算符「!!」強行放棄了非空判斷,開發者就得自己注意排雷了。否則的話,一旦出現空指針,App運行時依然會拋出異常。以下的演示代碼在運行時會扔出空指針異常,故而增加了異常捕獲處理:

btn_exclamation_two.setOnClickListener {

//!!表示不做非空判斷,強制執行後面的表達式,如果對象為空就會扔出空異常

//所以只有在確保為非空時,才能使用!!

try {

//即使返回給可空變數length_null,也會扔出異常

length = strB!!.length

tv_check_result.text = "使用!!得到字元串B的長度為$length"

} catch(e:Exception) {

tv_check_result.text = "發現空指針異常"

}

}

總結一下,Kotlin引入了空安全的概念,並在編譯時開展對象是否為空的校驗。相關的操作符說明概括如下:

1、聲明對象實例時,在類型名稱後面加問號,表示該對象可以為空;

2、調用對象方法時,在實例名稱後面加問號,表示一旦實例為空就返回null;

3、新引入運算符「?:」,一旦實例為空就返回該運算符右邊的表達式;

4、新引入運算符「!!」,通知編譯器不做非空校驗,運行時一旦發現實例為空就扔出異常;

持續更新,關注「老歐說安卓」,老歐和你一起探索移動互聯網的技術體系,認真寫作,用心學習。保留所有版權。

喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 公眾號 的精彩文章:

這才是真正的《我的前半生》、真正的亦舒
你真的以為門薩會員要拼智商?

TAG:公眾號 |

您可能感興趣

PowerQuery:空值運算的的解決思路
002航母近日海試,殲-15具備「防空值班」能力讓遼寧號升級戰鬥艦
夜觀天象,最近的天空值得一看