null安全能夠說是Kotlin語言對Java的重大改進之一,這樣能夠避免Java編程時使人恐懼的「NullPointerException」(簡稱:NPE)。但話說回來,null安全不過是各類現代語言玩剩下的東西。
1 非空類型和可空類型
下面先看一個簡單的例子。
程序清單:codes\02\2.8\NullableTest.kt
html
對比上面兩行粗體字代碼:第一行代碼聲明num的類型爲Int,第二行代碼聲明num的類型爲Int?。程序中第一行粗體字代碼沒法經過編譯,第二行粗體字代碼能經過編譯。其中Int?就是可空類型,這種類型的變量可接受Int值和null兩種狀況;而Int類型的變量則只接受Int值,不能接受null的狀況。因爲str是一個String變量,當程序試圖把String變量轉換爲Int值時,有可能轉換成功(若是變量值是形如"123"的字符串),也有可能轉換失敗(本程序就轉換失敗了)。轉換失敗時,就沒法成功返回Int值,此時將會返回null,所以必須使用Int?類型的變量來存儲轉換結果。
對於可能發生「值缺失」的狀況,編譯器會自動推斷該變量的類型爲可空類型。例如以下代碼:
java
須要指出的是,只有可空類型的變量或常量才能接受null,非空類型的變量和常量不能接受null。
Kotlin對可空類型進行了限制:若是不加任何處理,可空類型不容許直接調用方法、訪問屬性。所以,經過可空類型與非空類型的區分,Kotlin便可在程序中避免空指針異常。例如以下程序(程序清單同上):
編程
上面代碼中定義了aStr和bStr兩個變量,其中aStr是非空String類型,所以aStr變量不容許賦值爲null;而bStr是可空的String類型,bStr變量容許被賦值爲null;程序能夠直接調用aStr的方法和屬性,但aStr變量的值不可能爲nul的,所以能夠避免NPE;Kotlin對可空類型進行了限制,可空類型不容許直接調用方法和屬性,所以程序不能直接調用bStr的方法或屬性,這樣也可避免NPE。
2 先判斷後使用
可空類型的變量不容許直接調用方法或屬性,但能夠先判斷該變量不爲null,而後再去調用該變量的屬性和方法。例如以下代碼。
程序清單:codes\02\2.8\CheckNull.kt
f數組
上面程序定義了String?(可空類型)的變量b,這樣程序不能直接調用變量b的方法和屬性,Kotlin要求程序先判斷b不爲null,接下來程序便可在該條件下調用b的方法或屬性。
對於非空Boolean類型而言,它能夠接受3個值,true、false或null,所以對Boolean?類型變量進行判斷時,須要使用Boolean?變量顯式與true、false值進行比較。例如以下代碼。
f安全
編譯器會報錯:type mismatch: inferred type is Boolean? but Boolean was expected,這是由於Kotlin的if條件必須是Boolean類型,而Boolean?與Boolean本質上是兩個不一樣類型,所以編譯器會報錯。
3 安全調用
安全調用則在Java中已出現不少年了,只不過沒有出如今Java官方語法中——若是讀者熟悉Spring EL,必定見過以下用法。
user?.dog?.name
上面表達式語言表示若是user不爲null,則返回user的dog屬性;若是dog屬性值不爲null,則繼續獲取dog屬性值的name屬性值。反過來,若是user爲null,或user.dog爲null,上面表達式語言都不會致使NPE,而是整個表達式返回null——這就是Spring EL的安全調用。
Kotlin的安全調用也徹底與此相似,例如以下代碼。
程序清單:codes\02\2.8\SafeCall.kt
函數
上面程序中變量b的類型是String?,所以程序使用了?.安全調用來訪問b的length屬性,當b爲null時,程序也不會引起NPE,而是返回null。
與Spring EL相似的是,Kotlin的安全調用徹底也支持鏈式調用,就像Spring EL的用法同樣:
user?.dog?.name
上面代碼表示安全獲取user的dog的name屬性值,若是user或user.dog爲null,整個表達式將會返回null。
此外,安全調用還可與let全局函數結合使用,例如以下代碼(程序清單同上)。
post
上面粗體字代碼使用安全調用來調用let函數,這樣只有當item元素不爲null纔會執行let函數。上面程序調用let函數時傳入一個Lambda表達式做爲函數參數。
4 Elvis運算
Elvis運算也是一種早就滿大街的小技巧了,其實就是if else的簡化寫法。對好比下代碼。
程序清單:codes\02\2.8\Elvis.kt
指針
上面第一行粗體字代碼使用傳統if分支進行判斷,當b不爲null時返回b.length,不然返回-1;第二行粗體字代碼則使用?:運算符,該運算符就是Elvis——它的含義是:若是?:左邊的表達式不爲null時,則返回左邊表達式的值,不然返回?:右邊表達式的值。
因而可知?:其實就是if分支的簡化寫法。
此外,因爲Kotlin的return、throw都屬於表達式,所以它們也均可以用在?:運算符的右邊。例如以下代碼片斷:
val data = ……
val email = data["email"] ?: throw IllegalArgumentException("沒有指定Email信息!")
5 強制調用
強制調用時爲NPE愛好者準備的——若是讀者依然喜歡Java那種簡單、粗暴的方式:無論變量是否爲null,程序都直接調用該變量的方法或屬性,Kotlin也爲這種用法提供了支持,用!!.便可強制調用可空變量的方法或屬性,這樣強制調用可能引起NPE。例如以下代碼。
程序清單:codes\02\2.8\ForceCall.kt
code
上面程序就是將前面的安全調用程序中?.安全調用該爲!!.強制調用,當可空變量b爲null時,b!!.length將會引起NPE;與前面安全調用相似的是,強制調用也可做用於let()函數,此時無論item元素是否爲null,程序都會對該元素調用let()函數,所以也可能致使NPE。cdn