仅当所有参数都不为空时,Kotlin调用函数

时间:2018-06-07 13:08:56

标签: kotlin kotlin-null-safety

如果所有(或某些)参数都为空,kotlin中是否有一种方法可以阻止函数调用?例如具有功能:

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

如果参数为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
}

类似的东西(或类似的东西)是否可以减少调用者(可能是函数作者)的冗余代码?

5 个答案:

答案 0 :(得分:3)

如果您不希望调用者自己进行这些检查,您可以在中间函数中执行空检查,然后在它们通过时调用真实实现:

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功能。

答案 1 :(得分:3)

您可以为它定义自己的内联函数。

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

答案 2 :(得分:0)

如果您尝试将null分配给两个Int变量中的任何一个,您会发现这不起作用。请参阅注释中的编译错误。

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)无法使用nullInt?参数进行调用。如果你把Java放在混合中我怀疑没有null检查有一个安全的解决方案,因为你可以从Java端调用类型为test(Int, Int)的{​​{1}},这允许为null。与Integer“等效”的Java将是Int(这实际上不是空的安全)。

答案 3 :(得分:0)

NO!是你问题的答案(据我所知)

你最好的选择(假设功能没有暴露)就是你所说的。

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")
}

答案 4 :(得分:0)

让元组(对和三元组)

我认为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”替换为另一个单词(请参阅“三重扩展”作为指南),并且可以扩展到更大的元组(但Kotlin仅提供2个现成的框,可悲的是,我认为它不能通用)。