如何在单个表达式中检查一组函数是否全部 return 非空?

How can I check if a set of functions all return non null, in a single expression?

假设我有三个函数 foobarbaz,它们都是 return 可为 null 的类型。

fun foo(): Int? = 1
fun bar(): Int? = 2
fun baz(): Int? = 3

我想调用它们,如果它们都 return 非空,我想根据它们的 return 值计算一个值。

我可以用 语句 来做到这一点,像这样:

val x = foo()
val y = bar()
val z = baz()
val result = if (x != null && y != null && z != null) x + y + z else null

但是,我不喜欢这样的事实,即我必须声明 3 个我之后仍然可以访问的额外变量。通过有 3 个这样的额外语句,这也意味着我不能使用表达式主体函数,如果我正在编写一个 returns result.

的函数

如果我改用 lets:

val result = foo()?.let { x -> 
    bar()?.let { y -> 
        baz()?.let { z -> 
            x + y + z
        } 
    } 
}

这会创建一个很深的嵌套。如果只有一个函数就好了,但是如果有 3 个或更多函数,这就使我“调用这三个函数,如果它们都非 null,则将它们加在一起”的意图变得不明确。

我怎样才能用一种既能清楚表达我的意图又能使它成为一个单一表达的方式来写这篇文章?

val result = listOf(foo(), bar(), baz())
  .reduce { acc, i ->
    when {
      acc == null || i == null -> null
      else                     -> acc + i
    }
  }

或作为函数:

fun <T> apply(operation: (T, T) -> T, vararg values: T?): T? {
  return values
    .reduce { acc, i ->
      when {
        acc == null || i == null -> null
        else                     -> operation(acc, i)
      }
    }
}

val result = apply({ x, y -> x + y }, foo(), bar(), baz())

您可以过滤掉所有 null 值,并且只在列表上应用一个操作,如果它没有缩小大小,例如:

fun sumIfNoneNull(values: List<Int?>): Int? = values
    .filterNotNull()
    .takeIf { it.size == values.size }
    ?.sum()

人们可以进一步概括这一点,例如:

fun <T, R> List<T>.foldIfNoneNull(
    initial: R,
    operation: (acc: R, T) -> R
): R? = this
    .filterNotNull()
    .takeIf { nonNullList -> nonNullList.size == this.size }
    ?.fold(initial, operation)

您可以像使用其他任何东西一样使用它 fold,例如:

listOf(foo(), bar(), baz()).foldIfNoneNull(0) { acc, cur -> acc + cur }

如果它们是不同的类型,我认为你需要像这样编写自己的辅助函数(不同数量的参数需要不同的重载,因为编译器没有其他方法知道参数的类型) :

inline fun <T : Any, U : Any, R> ifAllNotNull(t: T?, u: U?, block: (t: T, u: U) -> R): R? {
    return when {
        t != null && u != null -> block(t, u)
        else -> null
    }
}

inline fun <T : Any, U : Any, V : Any, R> ifAllNotNull(t: T?, u: U?, v: V?, block: (t: T, u: U, v: V) -> R): R? {
    return when {
        t != null && u != null && v != null -> block(t, u, v)
        else -> null
    }
}
val result = ifAllNotNull(foo(), bar(), baz()) { x, y, z -> x + y + z }

请注意,在检查任何参数是否为空之前,将评估所有三个参数。


或者如果你想做你描述的(在结果计算后隐藏三个变量)只使用标准库函数,你可以使用run来限制临时变量的范围:

val result = run {
    val x = foo()
    val y = bar()
    val z = baz()
    if (x != null && y != null && z != null) x + y + z else null
}

如果您愿意,这也会让您有机会 short-circuit:

val result = run {
    val x = foo() ?: return@run null
    val y = bar() ?: return@run null
    val z = baz() ?: return@run null
    x + y + z
}