我們知道Java在開發及使用的過程中,存在空指針問題。一般出現在忘記給變量賦值或者因為某些原因調用了可能返回空置的方法,在調用的時候都會拋出空指針異常。
在Java 8之前需要我們編寫判斷空的邏輯代碼。
if (x != null) { x.method(...); }
在Java 8的時候,提供了Optional類來緩解此問題,但是也不能完全解決。
在談Kotlin的優勢的時候,大家都會想到空指針安全這一點,那么Kotlin又是如何避免這些問題的呢?Kotlin規定,在定義一個變量的時候,必須指定這個變量是否可以為空。這樣開發者能在編譯之前發現問題,並將錯誤扼殺在搖籃。
一、可空類型
默認聲明的變量是不能為 null 的,如果需要使變量能為 null, 需要在類型后添加 ?。例如:
val name: String = null // 編譯錯誤 var name = "harry" name = null // 編譯錯誤 // 正確的寫法 var name: String? = null var name: String? = "harry" name = null
同樣,方法的返回值,如果可能返回空,也要加 ?。
fun test(): String? = if ... else null
二、安全操作符
安全操作符的語法為:在對象后面添加 ? 作為后綴。安全操作符即可以用於訪問屬性,也可以用於調用方法。
Kotlin 規定在調用可空類型對象的方法的時候,必須使用安全操作符,否則編譯報錯。
安全操作符相當於在訪問對象時,先進行一次非空檢查。若為空值,則什么都不做,如果不為空值則繼續執行操作符后面的邏輯。
通過安全操作符,我們就可以安全訪問可空變量。例如:
var name: String? = "harry" if (name != null) { println(name.length) }
例如下面的邏輯,進行了多次空檢查,但代碼只需要一行,是不是很簡潔明了:
fun getCountryName(persion: Persion?) = persion?.city?.country?.name
三、非空斷言
當我們知道變量是不可能為 null 的, 可以用非空斷言 !!,這樣編譯器就不會檢查空指針,例如,下面我們可以通過非空斷言 !! 將可空對象賦值給非空變量:
val nullableString: String? = "hello world" val string: String = nullableString!! var name: String? = null name = "jason" val len = name!!.length
!! 非空斷言只是告訴編譯器可以在可空對象上進行訪問操作,它本身不會處理空值。所以,這種操作是不安全的,在使用的時候一定要注意。
五、Elvis操作
Java里面, 有個三元操作?:, 能夠實現 if … else … 的賦值。
Kotlin 也有這個操作符, 但是用法和效果不一樣,例如:
val nullableName: String? = ...
val name: String = nullableName ?: "default_name"
如上代碼意思了, 如果 nullableName 為 null, 就賦值 “default_name”。?:主要用於如果變量為 null就設置默認值。
六、安全類型轉換
如果想安全地進行類型轉換, 當轉換失敗時結果 null, 這種情況可用 as?。
val location: Any = "London" val safeString: String? = location as? String val safeInt: Int? = location as? Int
七、判空原理
為了更好的理解Kotlin空指針的原理。下面來看一下Kotlin是如何利用工具給開發者在編譯前給出提示的。
以下面的Java判斷空值方法為例:
public void foo(Bar bar) {}
對於這樣一個典型的方法,如果傳入的參數為null,那么通常的處理方式是檢查輸入:
public void foo(Bar bar) { if (bar == null) throw IllegalArgumentException(); }
如果調用該方法時傳入了null,那么它會拋出異常,並提供有用的信息。但這要到運行的時候才能看到。如果方法定義本身就能明確表達不接受null參數的意圖就好了。於是,上述代碼可以進一步改進為:
public void foo(@NotNull Bar bar) { if (bar == null) throw IllegalArgumentException() }
如此一來,像IntelliJ IDEA這樣的工具在檢測到調用者可能傳入null時就會提醒開發者。這樣的代碼沒錯,但就是有點啰嗦。
Kotlin采用了一種不同的null處理方式。它對可空類型和不可空類型作了區分,可空的類型后面會跟一個問號,比如Bar?,而Bar類型的變量則不可為空。於是,在Kotlin中,上述Java代碼就變成了下面這樣:
public fun foo(bar : Bar) {}
Kotlin非常簡潔且富有表現力。這從上面的例子可見一斑。
八、總結
Kotlin引入了空安全的概念,並在編譯時開展對象是否為空的校驗。相關的操作符說明概括如下:
1、聲明對象實例時,在類型名稱后面加問號,表示該對象可以為空;
2、調用對象方法時,在實例名稱后面加問號,表示一旦實例為空就返回null;
3、新引入運算符“?:”,一旦實例為空就返回該運算符右邊的表達式;
4、新引入運算符“!!”,通知編譯器不做非空校驗,運行時一旦發現實例為空就扔出異常;