如何使用Kotlin将函数输出转换为单位

q7solyqu  于 2022-12-13  发布在  Kotlin
关注(0)|答案(7)|浏览(172)

我在Kotlin中遇到了一个应该返回Unit的函数的问题,但是由于使用了另一个返回Boolean的函数,所以出现了类型不匹配。
下面是一个人为的例子:

fun printAndReturnTrue(bar: Int): Boolean {
    println(bar)
    return true
}

fun foo(bar: Int): Unit = when(bar) {
    0 -> println("0")
    else -> printAndReturnTrue(bar)
}

在这里,我实际上并不关心printAndReturnTrue返回布尔值的事实,我只想让foo执行副作用操作,但编译器会警告类型不匹配:我的else应该返回一个Unit值。
有没有一种很好的方法可以将值转换为Unit
我看到的最简单的解决方案是:

fun foo(bar: Int): Unit = when(bar) {
    0 -> println("0")
    else -> { 
        printAndReturnTrue(bar)
        Unit
    }
}

或:

fun foo(bar: Int): Unit = when(bar) {
    0 -> println("0")
    else -> eraseReturnValue(printAndReturnTrue(bar))
}

fun eraseReturnValue(value: Any) = Unit

或者我使用完整的函数形式:

fun foo(bar: Int): Unit { 
    when(bar) {
        0 -> println("0")
        else -> printAndReturnTrue(bar)
    }
}

我确信有一些惯用的方法可以做到这一点(或者是最后一个例子?),但目前我没有找到它们。

rqenqsqc

rqenqsqc1#

不幸的是,没有一个 * 惯用 * 的方法来做这个。一个类似的问题,将(T) -> Boolean类型的lambda传递给一个接受(T) -> Unit lambda的函数,以前已经出现过,并且那里所需要的自动转换应该在将来的某个时候出现在语言中。
现在,您可以在when表达式的末尾使用扩展函数,将其强制转换回Unit值:

fun Any?.toUnit() = Unit

fun foo(bar: Int): Unit = when(bar) {
    0 -> println("0")
    else -> printAndReturnTrue(bar)
}.toUnit()

或者,如果您更喜欢扩展属性,也可以使用扩展属性:

val Any?.unit get() = Unit

fun foo(bar: Int): Unit = when(bar) {
    0 -> println("0")
    else -> printAndReturnTrue(bar)
}.unit

当然,如果使用这两种类型中的任何一种,都可以省略函数的显式Unit返回类型。

t2a7ltrp

t2a7ltrp2#

我认为最后一种是最常用的,虽然你不需要显式的: Unit,但如果没有指定返回类型,它是块形式的默认值,如果你试图返回其他类型,你会得到一个错误。
但请注意一个微妙的细节:当when用作表达式时,除非编译器能够证明所有情况都得到处理,否则else是必需的;在块形式中,它“用作语句”,未处理的情况将被忽略。

r9f1avp5

r9f1avp53#

作为另一种选择,您可以创建一个更高阶的函数,该函数吞下返回值的函数的输出:

fun consume (fn: () -> Any): Unit {
  fn()
}

赠送:

fun foo(bar: Int): Unit = when(bar) {
    0 -> println("0")
    else -> consume { printAndReturnTrue(bar) }
}
6ovsh4lw

6ovsh4lw4#

如果我在一行中书写,我只使用分号。

someExpression; Unit
yjghlzjz

yjghlzjz5#

我认为你应该把函数的返回类型改为可选,这样更清楚,如下所示:

fun printAndReturnTrue(bar: Int): Boolean {
    println(bar)
    return true
}

fun foo(bar: Int): Unit? = when(bar) {
    0 -> println("0")
    else -> printAndReturnTrue(bar) as? Unit
}
3htmauhk

3htmauhk6#

你也可以这样做

fun foo(bar: Int): Unit = when(bar) {
    0 -> println("0")
    else -> printAndReturnTrue(bar).let{ Unit }
}
yxyvkwin

yxyvkwin7#

你可以直接在一个非单位值上调用.let {}来丢弃它(同时保留副作用):

// ...
else -> printAndReturnTrue(bar).let {}
// ...

{}是一个返回Unit的函数,它忽略了它的参数。let是一个扩展函数,它将它的接收方传递给给定的lambda --在本例中,lambda忽略它,并根据需要生成Unit。

相关问题