web-dev-qa-db-fra.com

L'appel de Kotlin ne fonctionne que si tous les arguments ne sont pas nuls

Existe-t-il un moyen dans kotlin d'empêcher l'appel de fonction si tous les arguments (ou certains) sont nuls? Par exemple avoir la fonction:

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

Je voudrais empêcher les contrôles nuls dans le cas où les arguments sont null. Par exemple, pour les arguments:

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

Je voudrais remplacer:

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

avec:

test(a, b)

J'imagine que la syntaxe de définition de fonction pourrait ressembler à ceci:

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

Et cela équivaudrait à:

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

    if(b == null) {
        return
    }

    // function body here
}

Est-ce que quelque chose comme ça (ou similaire) est possible pour réduire le code redondant de l'appelant (et éventuellement de l'auteur de la fonction)?

6
xinaiz

Si vous ne voulez pas que les appelants soient obligés de faire ces vérifications eux-mêmes, vous pouvez effectuer des vérifications nulles dans une fonction intermédiaire, puis faire appel à l'implémentation réelle quand ils ont réussi:

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

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

Edit: voici une implémentation de la fonction proposée par @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)

Bien sûr, vous devrez implémenter la fonction ifAllNonNull pour 2, 3, etc. paramètres si vous avez d’autres fonctions pour lesquelles vous avez besoin de ses fonctionnalités.

3
zsmb13

Vous pouvez définir votre propre fonction inline pour cela. 

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

Et puis vous pouvez l'appeler comme

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

NON! est la réponse à votre question (autant que je sache)

Votre meilleur pari (en supposant que la fonction n'est pas exposée) est ce que vous avez dit.

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

Si la fonction est exposée et peut être appelée sans ces contrôles, vous devez alors placer les contrôles dans votre fonction.

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

    println("Function was called")
}
0
Zachary Rudzik

Si vous essayez d’attribuer la valeur null à l’une des deux variables Int, vous constaterez que cela ne fonctionne pas. Voir les erreurs de compilation dans les commentaires.

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?
}

L'exemple montre que, dans un monde pur de Kotlin, test(a: Int, b: Int) ne peut pas être appelé avec les arguments null ou même Int?. Si vous mettez Java dans le mélange, je doute qu'il existe une solution sûre sans les contrôles null, car vous pouvez appeler test(Int, Int) avec le type Integer du côté Java, qui autorise null. L'équivalent Java de Int serait @NotNull Integer (ce qui n'est pas vraiment null-safe).

0
leonardkraemer