談談Kotlin:Kotlin每一行程式碼都有返回值
- 2019 年 10 月 6 日
- 筆記
?:
+let
實現的 if-else
?
這周在網上衝浪的時候,看到了這麼一個討論:「Elvis運算符與return組合的語句,在return前增加邏輯,如何寫得優雅?」,裡面提到一個「使用let
語法糖結合?:
運算符實現if-else
」的示例:
account?.let { it.hello() it.name = "Hello" } ?: run { logger.error("account is null") }
這裡藏著一個坑
乍一看,這種寫法很新穎很有創意,但實際上let
語法糖後接?:
這種做法是有問題的。
看下let
語法糖的函數聲明:public inline fun <T, R> T.let(block: (T) -> R): R

結合實現,可以看到,let
會在block
執行完後,返回block的返回值。
而Kotlin和Java不同,在Kotlin里每一行程式碼都是表達式,也就是說每一行程式碼執行完畢後都有一個返回值。
接下來考慮如下例子:
// 例1:可空變數為空 val nullVal: Any? = null nullVal?.let { println("[nullVal] not null code block") null } ?: run { println("[nullVal] null code block") } // 例2:可空變數為非空 val notnull: Any? = Any() notnull?.let { println("[notnull] not null code block") null } ?: run { println("[notnull] null code block") }
會得到如下輸出:
[nullVal] null code block [notnull] not null code block [notnull] null code block
例2的輸出顯然是不符合預期的。
在文章一開始的那個例子里,由於it.name = "Hello"
的返回值是Unit
,是一個非空的值,因此能夠如預期,呈現出和if-else
等價的效果,但這裡實際上會留下一個隱藏的坑。
寫程式碼的時候,肯定不會寫出我上面舉的例子那麼傻的程式碼,考慮如下變種:
fun test_let() { val nullable: Any? = null nullable?.let { println("[nullable] not null code block") maybeReturnNull(0) } ?: run { println("[nullable] null code block") } val notnull: Any? = Any() notnull?.let { println("[notnull] not null code block") maybeReturnNull(0) } ?: run { println("[notnull] null code block") } } private fun maybeReturnNull(count: Int): Any? = if (count % 2 == 0) null else Any()
一旦命中這樣的坑,查起來挺費勁的 QAQ
掉過這樣的坑後,就會發現樸素的if (xxx != null)
的寫法其實是最可愛的。
Tips
介紹一個小技巧:
IDEA編輯器提供了快速判空的模板,在變數後輸入.nn
回車。

就能收穫如下程式碼。

PS:nn
是notnull
的縮寫,輸入.notnull
也有同樣的功效。