一、 ?:
+ let
實作的 if-else
?
?:
let
if-else
這周在網上沖浪的時候,看到了這麼一T個讨論:“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裡每一行代碼都是表達式,也就是說每一行代碼執行完畢後都有一個傳回值。
接下來考慮如下例子:
```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
等價的效果,但這裡實際上會留下一個隐藏的坑。
寫代碼的時候,肯定不會寫出我上面舉的例子那麼傻的代碼,考慮如下變種:
```kotlin
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
也有同樣的功效。