Kotlin の複数変数 let 質問する

Kotlin の複数変数 let 質問する

Kotlin で複数の null 許容変数に対して複数の let を連鎖させる方法はありますか?

fun example(first: String?, second: String?) {
    first?.let {
        second?.let {
            // Do something just if both are != null
        }
    }
}

つまり、こんな感じです:

fun example(first: String?, second: String?) {
    first?.let && second?.let { 
        // Do something just if both are != null
    }
}

ベストアンサー1

使用するスタイル、すべてが同じタイプか異なるタイプか、リストの項目数が不明かどうかに応じて、いくつかのバリエーションを示します。

混合型、新しい値を計算するにはすべてが null であってはなりません

混合型の場合、各パラメータ数に対して、一見ばかげているように見えるものの、混合型に対して適切に動作する一連の関数を構築できます。

inline fun <T1: Any, T2: Any, R: Any> safeLet(p1: T1?, p2: T2?, block: (T1, T2)->R?): R? {
    return if (p1 != null && p2 != null) block(p1, p2) else null
}
inline fun <T1: Any, T2: Any, T3: Any, R: Any> safeLet(p1: T1?, p2: T2?, p3: T3?, block: (T1, T2, T3)->R?): R? {
    return if (p1 != null && p2 != null && p3 != null) block(p1, p2, p3) else null
}
inline fun <T1: Any, T2: Any, T3: Any, T4: Any, R: Any> safeLet(p1: T1?, p2: T2?, p3: T3?, p4: T4?, block: (T1, T2, T3, T4)->R?): R? {
    return if (p1 != null && p2 != null && p3 != null && p4 != null) block(p1, p2, p3, p4) else null
}
inline fun <T1: Any, T2: Any, T3: Any, T4: Any, T5: Any, R: Any> safeLet(p1: T1?, p2: T2?, p3: T3?, p4: T4?, p5: T5?, block: (T1, T2, T3, T4, T5)->R?): R? {
    return if (p1 != null && p2 != null && p3 != null && p4 != null && p5 != null) block(p1, p2, p3, p4, p5) else null
}
// ...keep going up to the parameter count you care about

使用例:

val risk = safeLet(person.name, person.age) { name, age ->
  // do something
}   

リストに null 項目がない場合にコード ブロックを実行する

ここでは 2 つの方法があります。1 つ目は、リストに null 以外の項目がすべて含まれている場合にコード ブロックを実行する方法、2 つ目は、リストに null 以外の項目が少なくとも 1 つ含まれている場合に同じことを実行する方法です。どちらの場合も、null 以外の項目のリストをコード ブロックに渡します。

機能:

fun <T: Any, R: Any> Collection<T?>.whenAllNotNull(block: (List<T>)->R) {
    if (this.all { it != null }) {
        block(this.filterNotNull()) // or do unsafe cast to non null collection
    }
}

fun <T: Any, R: Any> Collection<T?>.whenAnyNotNull(block: (List<T>)->R) {
    if (this.any { it != null }) {
        block(this.filterNotNull())
    }
}

使用例:

listOf("something", "else", "matters").whenAllNotNull {
    println(it.joinToString(" "))
} // output "something else matters"

listOf("something", null, "matters").whenAllNotNull {
    println(it.joinToString(" "))
} // no output

listOf("something", null, "matters").whenAnyNotNull {
    println(it.joinToString(" "))
} // output "something matters"

関数がアイテムのリストを受け取って同じ操作を実行するように少し変更します。

fun <T: Any, R: Any> whenAllNotNull(vararg options: T?, block: (List<T>)->R) {
    if (options.all { it != null }) {
        block(options.filterNotNull()) // or do unsafe cast to non null collection
    }
}

fun <T: Any, R: Any> whenAnyNotNull(vararg options: T?, block: (List<T>)->R) {
    if (options.any { it != null }) {
        block(options.filterNotNull())
    }
}

使用例:

whenAllNotNull("something", "else", "matters") {
    println(it.joinToString(" "))
} // output "something else matters"

これらのバリエーションは、 のような戻り値を持つように変更できますlet()

最初の非null項目を使用する(Coalesce)

SQL Coalesce 関数と同様に、最初の null 以外の項目を返します。関数には 2 つの種類があります。

fun <T: Any> coalesce(vararg options: T?): T? = options.firstOrNull { it != null }
fun <T: Any> Collection<T?>.coalesce(): T? = this.firstOrNull { it != null }

使用例:

coalesce(null, "something", null, "matters")?.let {
    it.length
} // result is 9, length of "something"

listOf(null, "something", null, "matters").coalesce()?.let {
    it.length
}  // result is 9, length of "something"

その他のバリエーション

...他にもバリエーションはありますが、より詳細な仕様があれば、範囲を絞り込むことができます。

おすすめ記事