Kotlin 做爲一門有着所謂空安全特性的(年輕)編程語言,有時出於實際業務場景須要仍是會把變量聲明成可空(Null-able)的,好在因爲空安全特性,編譯器會強制咱們對可空變量進行判空檢查(除非你使用了非空斷言 !! 強制讓編譯器閉嘴)。Kotlin 以徹底兼容 Java 爲設計原則,設計者們該是在設計階段就預見到了使用者們可能仍是須要進行不少判空檢查,因而引入了很是簡潔優雅的 Elvis 操做符,請看代碼:html
Java 典型判空操做:java
/** * <p>控制檯打印字符串的長度是多少</> * @param str:給定的字符串 */
public void askStringLength(String str){
int i;
//Java 中咱們通常這樣判空,或者使用那個三目運算符
if (str == null){
i = 0;
} else{
i = str.length();
}
//或者使用三目運算符
i = str == null? 0 : str.length();
System.out.println("The length is " + i);
}
複製代碼
如上,Java 沒有所謂空安全特性。你可能須要寫大量上面那樣的判空模板代碼,並且你確定沒法在每個變量可能爲空的地方加上這種防護性代碼,畢竟項目裏很容易積攢到成千上萬的變量,給這麼多變量寫上防護性代碼想一想都頭大!固然對於很大機率會拋出空指針異常的地方編譯器會作高亮提示,但會給出提示的地方放之整個項目而言實在微不足道。咱們手動 new 出來的對象和 null,做爲有着徹底不一樣行爲的兩種實體竟然能夠賦值給同一個變量,如此,若是在該判空的地方沒作判空,就是一個在運行時可能會拋出 NPE(NullPointerException) 的bug!編程
Kotlin 從新設計了一套徹底不一樣於 Java 的類型系統,聲稱可大大較少項目中的 NullPointerExceptio(Kotlin因以徹底兼容 Java 爲設計原則,於是沒法徹底杜絕 NPE)。就我本人經驗看來,效果還不賴,使用 Koltin 項目中的 NPE 確實少多了。並且 Koltin 中對可能拋出 NPE 的地方作判空檢查的方式比之 Java 而言實在優雅,今後遠離那些煩人的 if-else,只需藉助所謂 Elvis 操做符。安全
關於 Kotlin 的類型系統與 Java 之異同非本文重點,重點是在 Kotlin 中,若是一個變量是可空的,你在訪問此變量時若是不作判空檢查是通不過編譯的(除非你使用了非空斷言 !! 強制讓編譯器閉嘴)!如此使得咱們的代碼在運行時拋出 NPE 的機率大大減小!嗯能在編譯階段發現的問題就別留到運行時再發現!下面咱們直接看看上面包含判空操做的 Java 代碼其等價的 Koltin 代碼是怎麼樣的。編程語言
Kotlin 典型判空操做:ui
/** * * <p>控制檯打印字符串的長度是多少</> * @param str:給定的字符串 */
fun askStringLength(str: String?) {
val i: Int
// Kotlin 中咱們通常這樣對變量判空
// ?. 是 Kotlin 中所謂的安全調用操做符
// 若是 str 非空,就返回 str.length,不然返回 null,表達式(str?.length)返回的類型是 Int? 嗯有可能爲 null
// ?; 就是所謂的 Elvis 操做符
// 若是 ?: 左側表達式非空,elvis 操做符就返回其左側表達式,不然返回右側表達式
i = str?.length?:0//str.length 這麼寫是通不過編譯的
// 一路流式操做下來是否是感受很順暢,那可比 if-else 舒服多了!注意 Kotlin 中的 Elvis 操做符不是三目操做符,Kotlin 中是不存在三木操做符的
println("The length is $i")
}
複製代碼
Kotlin 中咱們通常這樣對變量判空:spa
i = str?.length?:0
複製代碼
?. 是 Kotlin 中所謂的安全調用操做符,若是 str 非空,就返回 str.length,不然返回 null,表達式(str?.length)返回的類型是 Int? , 嗯有可能爲 null,?; 就是所謂的 Elvis 操做符,若是 ?: 左側表達式非空,elvis 操做符就返回其左側表達式,不然返回右側表達式(作默認值)。.net
此時若是咱們有了新需求,要求計算一個 List 中全部字符串的長度之和,你極可能會這麼寫:設計
/** *<p>控制檯打印列表中全部字符串長度之和</> * @param strings:給定的字符串列表 */
fun askStringLength(strings: List<String?>) {
var i: Int = 0
strings.forEach { str ->
i = str?.length?:0 + i //注意這行代碼,固然你更可能寫成 i += str?.length?:0,爲了說明問題先按我寫的來
}
println("The length is $i")
}
複製代碼
若是真像上面那樣寫了,那你確定得不到正確的結果!你能夠運行試試!指針
緣由在於 Elvis 操做符的優先級較低,請看下方我從 Kotlin 官網扒來的操做符優先級表:
Precedence | Title | Symbols |
---|---|---|
Highest | Postfix | ++ , -- , . , ?. , ? |
Prefix | - , + , ++ , -- , ! , label |
|
Type RHS | : , as , as? |
|
Multiplicative | * , / , % |
|
Additive | + , - |
|
Range | .. |
|
Infix function | simpleIdentifier |
|
Elvis | ?: |
|
Named checks | in , !in , is , !is |
|
Comparison | < , > , <= , >= |
|
Equality | == , !== |
|
Conjunction | && |
|
Disjunction | || |
|
Lowest | Assignment | = , += , -= , *= , /= , %= |
注意 Elvis 操做符的位置!
這行代碼:
i = str?.length?:0 + i
複製代碼
其實等價於
i = str?.length ?: (0 + i)
複製代碼
因此想讓代碼正確運行咱們其實應該寫成這樣:
i = (str?.length?:0) + i
複製代碼
使用時千萬要注意操做符的優先級!
完。