web-dev-qa-db-ja.com

Kotlinは、すべての引数がnullでない場合にのみ関数を呼び出します

すべて(または一部)の引数がnullの場合に関数呼び出しを防ぐ方法はkotlinにありますか?たとえば、機能を持つ:

fun test(a: Int, b: Int) { /* function body here */ }

引数がnullの場合のnullチェックを防ぎたいのですが。たとえば、引数の場合:

val a: Int? = null
val b: Int? = null 

交換したい:

a?.let { b?.let { test(a, b) } }

と:

test(a, b)

関数定義構文は次のようになると思います。

fun test(@PreventNullCall a: Int, @PreventNullCall b: Int)

そして、それは以下と同等です:

fun test(a: Int?, b: Int?) {
    if(a == null) {
        return
    }

    if(b == null) {
        return
    }

    // function body here
}

そのような(または同様の)呼び出し元(およびおそらく関数の作成者)の冗長なコードを削減することは可能ですか?

15
xinaiz

独自のインライン関数を定義できます。

inline fun <R, A> ifNotNull(a: A?, block: (A) -> R): R? =
    if (a != null) {
        block(a)
    } else null

inline fun <R, A, B> ifNotNull(a: A?, b: B?, block: (A, B) -> R): R? =
    if (a != null && b != null) {
        block(a, b)
    } else null

inline fun <R, A, B, C> ifNotNull(a: A?, b: B?, c: C?, block: (A, B, C) -> R): R? =
    if (a != null && b != null && c != null) {
        block(a, b, c)
    } else null

inline fun <R, A, B, C, D> ifNotNull(a: A?, b: B?, c: C?, d: D?, block: (A, B, C, D) -> R): R? =
    if (a != null && b != null && c != null && d != null) {
        block(a, b, c, d)
    } else null

そして、あなたはそれを次のように呼ぶことができます

ifNotNull(a, b, c, d) { a, b, c, d ->
    ...
}
3
EpicPandaForce

呼び出し側がこれらのチェックを自分で行う必要がないようにしたい場合は、中間関数でnullチェックを実行し、渡されたときに実際の実装を呼び出すことができます。

fun test(a: Int?, b: Int?) {
    a ?: return
    b ?: return
    realTest(a, b)
}

private fun realTest(a: Int, b: Int) {
    // use params
}

編集:@Alexey Romanovが提案した関数の実装を以下に示します。

inline fun <T1, T2, R> ifAllNonNull(p1: T1?, p2: T2?, function: (T1, T2) -> R): R? {
    p1 ?: return null
    p2 ?: return null
    return function(p1, p2)
}

fun test(a: Int, b: Int) {
    println("$a, $b")
}

val a: Int? = 10
val b: Int? = 5
ifAllNonNull(a, b, ::test)

もちろん、機能が必要な他の関数がある場合は、2、3などのパラメーターにifAllNonNull関数を実装する必要があります。

3
zsmb13

番号!あなたの質問に対する答えです(私の知る限り)

あなたの最善の策(関数が公開されていないと想定)は、あなたが言ったことです。

a?.let { b?.let { test(a, b) } }

関数が公開されており、これらのチェックなしで呼び出される可能性がある場合は、関数内にチェックを配置する必要があります。

fun test(a: Int?, b: Int?) {
    if (listOf(a, b).filterNotNull().size < 2) return

    println("Function was called")
}
1
Zachary Thomas

タプルを許可(ペア&トリプル)

OPの精神は構文だったと思うので、私の答えはタプル型に「let」を提供することに焦点を当てています。

使用例:

fun main() {
    val p1: Int? = 10 // or null
    val p2: Int? = 20 // or null
    val p3: Int? = 30 // or null

    val example1 = (p1 to p2).let(::testDouble)
    val example2 = (p1 to p2).let { a, b -> a * b }

    val example3 = (p1 to p2 to p3).let(::testTriple)
    val example4 = (p1 to p2 to p3).let { a, b, c -> a * b * c }
}

fun testDouble(a: Int, b: Int): Int {
    return a + b
}

fun testTriple(a: Int, b: Int, c: Int): Int {
    return a + b + c
}

拡張機能:

// Define let for Pair & Triple
fun <P1, P2, R> Pair<P1?, P2?>.let(f: (P1, P2) -> R): R? {
    return f(first ?: return null, second ?: return null)
}

fun <P1, P2, P3, R> Triple<P1?, P2?, P3?>.let(f: (P1, P2, P3) -> R): R? {
    return f(first ?: return null, second ?: return null, third ?: return null)
}

// Cute "to" syntax for Triple
infix fun <P1, P2, P3> Pair<P1?, P2?>.to(third: P3?): Triple<P1?, P2?, P3?> {
    return Triple(first, second, third)
}

「to」を別のWordに置き換えることができ(ガイドとしてのトリプル拡張を参照)、より大きなタプルに拡張することもできます(ただし、Kotlinはすぐに使用できる2つのみを提供しますが、残念ながら、それは一般的ではないと思います)。 。

0
AjahnCharles

2つのInt変数のいずれかにnullを割り当てようとすると、これが機能しないことがわかります。コメントのコンパイルエラーを参照してください。

_fun test(a: Int, b: Int) { /* function body here */ }

fun main(){
    test(null, 0) //Null can not be value of non-null type Int
    val b : Int? = null
    test(0, b) // Type mismatch. Required: Int - Found: Int?
}
_

この例は、純粋なKotlinの世界では、test(a: Int, b: Int)nullまたは_Int?_引数で呼び出すことはできないことを示しています。 Javaを組み合わせて使用​​する場合、Java側からnull型でtest(Int, Int)を呼び出すことができるため、Integerチェックなしで安全な解決策があるとは思えません。 Intと同等のJavaは_@NotNull Integer_です(実際にはnullセーフではありません)。

0
leonardkraemer