为什么在Java中实现返回单元的Kotlin函数时必须返回Unit.INSTANCE?

Ran*_*ku' 38 java kotlin kotlin-interop

如果我有Kotlin功能

fun f(cb: (Int) -> Unit)
Run Code Online (Sandbox Code Playgroud)

我想用fJava 调用,我必须这样做:

f(i -> {
     dosomething();
     return Unit.INSTANCE;
});
Run Code Online (Sandbox Code Playgroud)

这看起来很丑陋.为什么我不能这样写f(i -> dosomething());,因为Unit在Kotlin中相当于voidJava?

Kir*_*man 45

Unit在Kotlin中,大多数情况下void与Java相同,但只有当JVM的规则允许时才这样.

Kotlin中的功能类型由以下接口表示:

public interface Function1<in P1, out R> : Function<R> {
    /** Invokes the function with the specified argument. */
    public operator fun invoke(p1: P1): R
}
Run Code Online (Sandbox Code Playgroud)

当您声明时(Int) -> Unit,从Java的角度来看,这相当于Function<Integer, Unit>.这就是你必须返回一个值的原因.要解决这个问题,在Java中有两个独立的接口Consumer<T>,Function<T, R>当你没有/有一个返回值时.

Kotlin设计师决定放弃功能接口的重复,而是依赖编译器"魔术".如果在Kotlin中声明lambda,则不必返回值,因为编译器会为您插入一个值.

为了使您的生活更轻松一点,你可以写一个封装了一个辅助方法Consumer<T>Function1<T, Unit>:

public class FunctionalUtils {
    public static <T> Function1<T, Unit> fromConsumer(Consumer<T> callable) {
        return t -> {
            callable.accept(t);
            return Unit.INSTANCE;
        };
    }
}
Run Code Online (Sandbox Code Playgroud)

用法:

f(fromConsumer(integer -> doSomething()));
Run Code Online (Sandbox Code Playgroud)

有趣的事实:UnitKotlin编译器的特殊处理是您可以编写如下代码的原因:

fun foo() {
    return Unit
}
Run Code Online (Sandbox Code Playgroud)

要么

fun bar() = println("Hello World")
Run Code Online (Sandbox Code Playgroud)

这两种方法void在生成的字节码中都有返回类型,但编译器足够聪明,可以解决这个问题并允许您使用return语句/表达式.

  • 很难相信,编译器无法弄清楚最后的例子出来没有一个真正的单元实例. (9认同)