談談Kotlin:Kotlin每一行代碼都有返回值

?:+let 實現的 if-else

這周在網上衝浪的時候,看到了這麼一T個討論:「Elvis運算符與return組合的語句,在return前增長邏輯,如何寫得優雅?」,裏面提到一個「使用let語法糖結合?:運算符實現if-else」的示例:bash

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 語法糖

結合實現,能夠看到,let會在block執行完後,返回block的返回值。spa

而Kotlin和Java不一樣,在Kotlin裏每一行代碼都是表達式,也就是說每一行代碼執行完畢後都有一個返回值。code

接下來考慮以下例子:cdn

// 例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")
}
複製代碼

會獲得以下輸出:blog

[nullVal] null code block

[notnull] not null code block
[notnull] null code block
複製代碼

例2的輸出顯然是不符合預期的。ip

在文章一開始的那個例子裏,因爲it.name = "Hello"的返回值是Unit,是一個非空的值,所以可以如預期,呈現出和if-else等價的效果,但這裏實際上會留下一個隱藏的坑。string

寫代碼的時候,確定不會寫出我上面舉的例子那麼傻的代碼,考慮以下變種:it

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:nnnotnull的縮寫,輸入.notnull也有一樣的功效。

相關文章
相關標籤/搜索