17

如果所有(或某些)参数为空,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
}

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

4

5 回答 5

6

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

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)

当然ifAllNonNull,如果您有其他需要其功能的功能,则需要实现 2、3 等参数的功能。

于 2018-06-07T13:25:51.763 回答
4

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

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 ->
    ...
}
于 2018-06-07T17:47:06.253 回答
1

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

您最好的选择(假设该功能未公开)就是您所说的。

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")
}
于 2018-06-07T18:19:54.313 回答
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)中,不能使用null甚至Int?参数调用。如果你把 Java 混在一起,我怀疑没有null检查的安全解决方案,因为你可以从 Java 端调用test(Int, Int)类型Integer,它允许 null。Java“等价于”Int将是@NotNull Integer(这不是真正的空安全)。

于 2018-06-07T13:25:38.000 回答
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”替换为另一个词(请参阅 Triple extension 作为指南),并且您可以扩展到更大的元组(但 Kotlin 仅提供 2 个开箱即用的 & 遗憾的是我不认为它可以是通用的) .

于 2019-04-16T13:03:10.017 回答