Kotlin null的處理詳解
Kotlin null的處理詳解
NullPointerException,俗稱NPE,不管菜鳥還是老鳥們,都是不可避免,經(jīng)常遇到的一個(gè)異常,解釋起來很簡單,就“空指針”三個(gè)字??偸窃谝淮尾恍⌒模暨M(jìn)這個(gè)陷阱里。Kotlin 的設(shè)計(jì)目標(biāo)就是希望消除代碼中 null 引用帶來的危險(xiǎn), 也就是所謂的造成十億美元損失的大錯(cuò)誤.
NPE的原因
盡管Kotlin希望消除代碼中的NPE,我們總是不小心,總會(huì)不小心又掉進(jìn)NPE的陷阱,下面是可能NPE的原因:
- 明確調(diào)用 throw NullPointerException()
- 使用 !! 操作符
- 外部的 Java 代碼導(dǎo)致這個(gè)異常
- 初始化過程中存在某些數(shù)據(jù)不一致 (在構(gòu)造器中使用了未初始化的 this)
避免NPE
在Kotlin中,明確區(qū)分可以指向 null 的引用 (可為 null 引用) 與不可以指向null的引用 (非null引用). 比如, 一個(gè)通常的 String 類型變量不可以指向 null:
var a: String = "abc" a = null // 編譯錯(cuò)誤
此時(shí),你對a變量的任何調(diào)用都是安全的,因?yàn)樗鼮榉莕ull,你可以對該引用做任何操作而不會(huì)報(bào)NPE。就算對a賦值給Null,編譯器都會(huì)報(bào)錯(cuò),不會(huì)讓你通過。
可是,實(shí)際開發(fā)時(shí),不可能所有的變量不會(huì)null,總會(huì)有情況,變量是null的,此時(shí)該如何設(shè)定該變量呢?要允許null值的變量, 我們可以將變量聲明為可為null的字符串, 寫作 String? :
var b: String? = "abc" b = null // ok
這樣,我們是解決了變量可以設(shè)置為null的問題,可NPE的這個(gè)陷阱又設(shè)上了,如果再粗心大意,NPE又來找麻煩了。
如果我們?nèi)匀恍枰L問這個(gè)屬性, 對不對? 有以下幾種方法可以實(shí)現(xiàn):
在條件語句中進(jìn)行null檢查
通過條件語句,對變量檢查是否為null,對 null 和非 null 的兩種情況分別處理:
if (b != null && b.length > 0) print("String of length ${b.length}") else print("Empty string")
注:
該方案只是針對當(dāng)前b的值,b的值在檢查以后,如果b的值被修改仍需對此值做非null檢查,也就是說每次修改b的值,都必須對b作非null驗(yàn)證,這也導(dǎo)致了代碼的冗余。
安全調(diào)用
什么是安全調(diào)用?看起來有點(diǎn)不理解,既然是null怎么還會(huì)有安全呢?
b?.length
在Kotlin中,允許使用 “?.”操作符調(diào)用變量,其含義是如果b不是null,這個(gè)表達(dá)式將會(huì)返回b.length,否則返回 null.如果使用了”?.”,其表達(dá)式的值也應(yīng)為 可為null的,比如Int?,否則編譯器會(huì)報(bào)錯(cuò)。
bob?.department?.head?.name
安全調(diào)用在鏈?zhǔn)秸{(diào)用的情況下非常有用.這樣的鏈?zhǔn)秸{(diào)用, 只要屬性鏈中任何一個(gè)屬性為 null, 整個(gè)表達(dá)式就會(huì)返回 null.
?:操作符
如果 ?: 左側(cè)的表達(dá)式值不是null, 就會(huì)返回表達(dá)式的的值,否則, 返回右側(cè)表達(dá)式的值.
val l = b?.length ?: -1
如果b不為null,將返回b的長度,如果為null,將返回-1
注:
只有在左側(cè)表達(dá)式值為 null 時(shí), 才會(huì)計(jì)算右側(cè)表達(dá)式.
由于在 Kotlin 中 throw 和 return 都是表達(dá)式, 因此它們也可以用在 Elvis 操作符的右側(cè). 這種用法可以帶來很大的方便, 比如, 可以用來檢查函數(shù)參數(shù)值是否合法。
fun foo(node: Node): String? { val parent = node.getParent() ?: return null val name = node.getName() ?: throw IllegalArgumentException(“name expected”) // … }
!! 操作符
對于b不為null的情況, 這個(gè)表達(dá)式將會(huì)返回這個(gè)非null值(比如, 在我們的例子中就是一個(gè) String 類型值), 如果b是 null, 這個(gè)表達(dá)式就
會(huì)拋出一個(gè) NPE:
val l = b!!.length()
當(dāng)b為null時(shí)就拋出一個(gè)異常,你可以捕獲它,而不是在不知道在某一角落里調(diào)用時(shí),才報(bào)出異常,抓頭撓腮半天,才找到NPE在哪里。
安全的類型轉(zhuǎn)換
如果對象不是我們期望的目標(biāo)類型, 那么通常的類型轉(zhuǎn)換就會(huì)導(dǎo)致ClassCastException,可以選擇使用安全的類型轉(zhuǎn)換, 如果轉(zhuǎn)換不成功, 它將會(huì)返回 null,這樣避免了轉(zhuǎn)換異常的拋出。
val aInt: Int? = a as? Int
感謝閱讀,希望能幫助到大家,謝謝大家對本站的支持!
相關(guān)文章
深入學(xué)習(xí)MyBatis中的參數(shù)(推薦)
大家日常使用MyBatis經(jīng)常會(huì)遇到一些異常,想要避免參數(shù)引起的錯(cuò)誤,我們需要深入了解參數(shù)。想了解參數(shù),我們首先看MyBatis處理參數(shù)和使用參數(shù)的全部過程。下面這篇文章主要給大家介紹了MyBatis中參數(shù)的的相關(guān)資料,需要的朋友可以參考借鑒,下面來一起看看吧。2017-06-06SpringBoot2 整合FreeMarker實(shí)現(xiàn)頁面靜態(tài)化示例詳解
這篇文章主要介紹了SpringBoot2 整合FreeMarker實(shí)現(xiàn)頁面靜態(tài)化示例詳解,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-07-07MybatisPlus中插入數(shù)據(jù)后獲取該對象主鍵值的實(shí)現(xiàn)
這篇文章主要介紹了MybatisPlus中插入數(shù)據(jù)后獲取該對象主鍵值,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-09-09Java object wait notify notifyAll代碼解析
這篇文章主要介紹了Java object wait notify notifyAll代碼解析,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-11-11Spring boot實(shí)現(xiàn)熱部署的兩種方式詳解
這篇文章主要介紹了Spring boot實(shí)現(xiàn)熱部署的兩種方式,這兩種方法分別是使用 Spring Loaded和使用spring-boot-devtools進(jìn)行熱部署,文中給出了詳細(xì)示例代碼和介紹,需要的朋友可以參考學(xué)習(xí),下面來一起看看吧。2017-04-04java 多線程實(shí)現(xiàn)在線咨詢(udp)
這篇文章主要介紹了java 多線程實(shí)現(xiàn)在線咨詢(udp)的示例,幫助大家更好的理解和學(xué)習(xí)Java 網(wǎng)絡(luò)編程的相關(guān)內(nèi)容,感興趣的朋友可以了解下2020-11-11Java項(xiàng)目導(dǎo)出數(shù)據(jù)為 PDF 文件的操作代碼
一個(gè)小需求,需要將頁面上的數(shù)據(jù)導(dǎo)出為PDF,正常情況下這個(gè)需求需要讓前端來做,但是現(xiàn)在上面讓咱們后端來做,也沒問題,這篇文章主要介紹了Java項(xiàng)目導(dǎo)出數(shù)據(jù)為 PDF 文件的操作代碼,需要的朋友可以參考下2022-12-12