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

2022-08-31 15:28:31

如果我有一个 Kotlin 函数

fun f(cb: (Int) -> Unit)

我想从Java调用,我必须这样做:f

f(i -> {
     dosomething();
     return Unit.INSTANCE;
});

这看起来很丑陋。为什么我不能像 这样写,因为在 Kotlin 中等同于在 Java 中?f(i -> dosomething());Unitvoid


答案 1

Unit在Kotlin中主要等同于Java,但只有当JVM的规则允许它时。void

Kotlin 中的函数类型由以下接口表示:

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

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

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

为了让你的生活更轻松一点,你可以写一个帮助器方法,将 a 包装在 :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;
        };
    }
}

用法:

f(fromConsumer(integer -> doSomething()));

有趣的事实:Kotlin 编译器对 Kotlin 的特殊处理是你可以编写如下代码的原因:Unit

fun foo() {
    return Unit
}

fun bar() = println("Hello World")

这两种方法在生成的字节码中都有返回类型,但编译器足够聪明,可以弄清楚这一点,并允许您无论如何都使用返回语句/表达式。void


答案 2

我在Kotlin和Java中使用这种方法。MyKotlinClass的方法,您将在Java中看到,在Kotlin中您将看到两种方法(类方法+扩展函数)。

MyKotlinClass {

  //Method to use in Java, but not restricted to use in Kotlin.
    fun f(cb: Consumer<Int>) { //Java8 Consumer, or any custom with the same interface
      int i = getYourInt()
      cb.accept(i)
    }
}

//Extension for Kotlin. It will be used in Kotlin.
fun MyKotlinClass.f(cb: (Int) -> Unit) {
    f(Consumer { cb(it) })
}

推荐