If I have a Kotlin function
fun f(cb: (Int) -> Unit)
and I want to call f
from Java, I have to do it like:
f(i -> {
dosomething();
return Unit.INSTANCE;
});
which looks very ugly. Why can't I just write it like f(i -> dosomething());
, since Unit
in Kotlin is equivalent to void
in Java?
Unit
in Kotlin is mostly equivalent to void
in Java, however only when the rules of the JVM allow it.
Functional types in Kotlin are represented by interfaces like:
public interface Function1<in P1, out R> : Function<R> {
/** Invokes the function with the specified argument. */
public operator fun invoke(p1: P1): R
}
When you declare (Int) -> Unit
, from Java's point of view this is equivalent to Function<Integer, Unit>
. That's why you have to return a value. To work around this problem, in Java there are two separate interfaces Consumer<T>
and Function<T, R>
for when you don't have/have a return value.
The Kotlin designers decided to forgo the duplication of functional interfaces and instead rely on compiler "magic". If you declare a lambda in Kotlin, you don't have to return a value because the compiler will insert one for you.
To make your life a little bit easier, you can write a helper method that wraps a Consumer<T>
in a Function1<T, Unit>
:
public class FunctionalUtils {
public static <T> Function1<T, Unit> fromConsumer(Consumer<T> callable) {
return t -> {
callable.accept(t);
return Unit.INSTANCE;
};
}
}
Usage:
f(fromConsumer(integer -> doSomething()));
Fun fact: The special handling of Unit
by the Kotlin compiler is the reason you can write code like:
fun foo() {
return Unit
}
or
fun bar() = println("Hello World")
Both methods have return type void
in the generated bytecode but the compiler is smart enough to figure that out and allow you to use return statements/expressions anyway.