Kotlin刨根問底(一):你真的瞭解Kotlin中的空安全嗎?

這篇以前寫過,刪了一些廢話,修正了一些錯誤,弄了下排版,最近開始連載這個系列,
行文結構:要點提煉(方便回顧) + 常規操做 + 源碼層面摸索實現原理
內容部分摘取自:《Kotlin實用指南》,感興趣的能夠訂閱一波 ~web


0x一、要點提煉


  • Kotlin中經過「非空類型」與「可空類型」來規避 NPE
  • 可空操做符「`?`」、安全調用操做符「`?.`」、非空斷言運算符「`!!`」
  • Elvis操做符「`?:`」若是不爲空返回它,不然返回另外一個值;
  • 安全的類型轉換「`as?`」
  • ?.let{}
  • 不是絕對的空指針安全:Kotlin調用返回空的Java代碼;
  • Kotlin空安全的實現原理:


0x二、Kotlin裏的空安全用法


NullPointerException(NPE),空指針異常,發生在運行時引用一個空對象的方法時會拋出apache

Java中常見的規避手段是:編程

在對象使用前進行判空:if (obj != null)安全

多層判空時顯得不是很美觀,而Kotlin中是「空安全」的:app

編譯期 就處理Null編程語言

空安全不是Kotlin特有的,其餘不少編程語言也有,下面簡述下Kotlin空安全的相關用法。函數


① 非空類型與可空類型

Kotlin中經過「非空類型」和「可空類型」來規避NPE,非空類型不能設置爲Null值:post

可空類型能夠設置爲Null值,在類型後加上 可空操做符(?) 便可,代碼示例以下: ui

可空類型,直接訪問它的屬性或方法,會報錯:spa

能夠經過 安全調用操做符(?.)非空斷言運算符(!!) 來解決,代碼示例以下:

運行結果以下


② Elvis操做符(?:)

三目條件運算符的簡略寫法:若是不是空,就返回它,不然返回另外一個值。代碼示例以下:


③ 安全的類型轉換

Kotlin中能夠使用as關鍵字來進行類型轉換,而使用as?表示安全類型的轉換。最多見的使用場合,後臺接口返回一個參數,須要咱們本身作下類型轉換,直接用as的話,若是參數爲null或者非String類型,就可能引起異常,若是用as? ,參數異常則不會轉換,代碼示例以下:

運行結果以下


④ ?.let{}

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過濾出非空元素。


0x三、不是絕對的空指針安全


如題,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註解來解決~


0x四、Kotlin是如何實現空安全的


接着咱們來探究下,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;

參考文獻


相關文章
相關標籤/搜索