這篇以前寫過,刪了一些廢話,修正了一些錯誤,弄了下排版,最近開始連載這個系列,
行文結構:要點提煉(方便回顧) + 常規操做 + 源碼層面摸索實現原理,
內容部分摘取自:《Kotlin實用指南》,感興趣的能夠訂閱一波 ~web
NullPointerException(NPE),空指針異常,發生在運行時,引用一個空對象的方法時會拋出。apache
Java中常見的規避手段是:編程
在對象使用前進行判空:if (obj != null)安全
多層判空時顯得不是很美觀,而Kotlin中是「空安全」的:app
編譯期 就處理Null編程語言
空安全不是Kotlin特有的,其餘不少編程語言也有,下面簡述下Kotlin空安全的相關用法。函數
Kotlin中經過「非空類型」和「可空類型」來規避NPE,非空類型不能設置爲Null值:post
可空類型能夠設置爲Null值,在類型後加上 可空操做符(?) 便可,代碼示例以下: ui
可空類型,直接訪問它的屬性或方法,會報錯:spa
能夠經過 安全調用操做符(?.) 或 非空斷言運算符(!!) 來解決,代碼示例以下:
運行結果以下:
三目條件運算符的簡略寫法:若是不是空,就返回它,不然返回另外一個值。代碼示例以下:
Kotlin中能夠使用as
關鍵字來進行類型轉換,而使用as?
表示安全類型的轉換。最多見的使用場合,後臺接口返回一個參數,須要咱們本身作下類型轉換,直接用as的話,若是參數爲null或者非String類型,就可能引起異常,若是用as? ,參數異常則不會轉換,代碼示例以下:
運行結果以下:
let函數除了可用於在同一個做用域下操做變量外(代碼更優雅)外:
tvTitle.let {
it.text = "標題"
it.textSize = 18.0f
it.gravity = Gravity.LEFT or Gravity.CENTER
it.setOnClickListener {}
}
複製代碼
還能夠用做作判null操做,好比下面這樣的代碼:
test咱們已經設置了一個值,可是仍是報錯,添加上一個非空斷言便可:
還有個更優雅的寫代碼方式,就是用2,修改後的代碼以下:
另外?.let{}遍歷集合會忽略null值,只對非null值執行操做;除此以外,還能夠用集合操做函數filterNotNull過濾出非空元素。
如題,Kotlin中並非絕對的空指針安全,最多見的就是在Kotlin去調Java代碼,好比下面這個例子:
//Java
public class Test {
public static String getMsg() {
return null;
}
}
//Kotlin
fun main() {
println(Test.getMsg().length)
}
複製代碼
運行後就直接報錯:
當你與Java代碼進行互操做時,Null安全性確實被破壞了,固然想規避這個問題也很簡單,加個?便可,示例以下:
fun main() {
println(Test.getMsg()?.length)
}
複製代碼
運行結果以下:
Tips:也能夠爲Java代碼@NotNull註解來解決~
接着咱們來探究下,Kotlin中究竟是怎麼實現空安全的,寫下簡單的代碼:
fun test_1(str: String) = str.length
fun test_2(str: String?) = str?.length
fun test_3(str: String?) = str!!.length
fun test_4(str: Any?) { str as String }
fun test_5(str: Any?) { str as? String }
複製代碼
接着依次點擊:Tools -> Kotlin -> Show Kotlin Bytecode,生成字節碼:
生成後的Java代碼以下:
接着分析一波,首先是test_1函數,能夠看到這裏有一個@NotNull的註解,而後是:
Intrinsics.checkParameterIsNotNull(str, "str");
複製代碼
Intrinsics是Kotlin的一個內部類,定位到checkParameterIsNotNull函數:
再跟:
行吧,其實就是直接對參數判空,若是爲空拋出參數爲空的異常。接着看下test_2函數,比較簡單,判斷是否爲空,不爲空的話,調用對應的方法,不然返回一個null。再接着是test_3函數,直接判斷是否爲空,空的話直接拋出Npe異常。
而後是test_4函數,判空,若是空拋出類型轉換異常,不然執行後續代碼;最後是test_5函數,建立一個新對象把參數的值傳給他,而後進行類型判斷,若是不是特定類型,對象賦值null,而後把執行類型強轉後的對象賦值給一個新的對象。
綜上,Kotlin中對空安全背後的處理套路以下:
- 一、非空類型的屬性編譯器添加@NotNull註解,可空類型添加@Nullable註解;
- 二、非空類型直接對參數進行判空,若是爲空直接拋出異常;
- 三、可空類型,若是是?.判空,不空才執行後續代碼,不然返回null;若是是!!,空的話直接拋出NPE異常。
- 四、as操做符會判空,空的話直接拋出異常,不爲空才執行後續操做,沒作類型判斷!運行時可能會報錯!
- 五、as?則是新建一個變量,參數賦值給它,而後判斷是否爲特定類型,賦值爲null,接着把這個變量的值賦值給另外一個新的變量,這裏有一點注意:as?處理後的參數可能爲空!!!因此調用as?轉換後的對象還須要添加安全調用操做符(?.)
提醒:(這裏直接用編譯後Java代碼的緣由是比較直觀~)
- @NotNull註解對應字節碼裏的:@Lorg/jetbrains/annotations/NotNull;
- @Nullable註解對應字節碼裏的:@Lorg/jetbrains/annotations/Nullable;
參考文獻: