71

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?

Randy Sugianto 'Yuku'
  • 71,383
  • 57
  • 178
  • 228
  • 1
    This is ugly and makes me not even want to use Kotlin. I keep seeing these weird things over and over as I use the language more and more. –  Apr 02 '18 at 17:33
  • 14
    It is because for Java interop. Kotlin itself is nice and quite elegant. – Randy Sugianto 'Yuku' Apr 03 '18 at 05:03
  • @It'sYourAppLLC You can see this only when you are using Kotlin code in Java code. Easy fix to it is to eliminate the use of Java code and write all in Kotlin. There is no more reason to use Java anymore when we have Kotlin. IMHO. – mojmir.novak Dec 09 '20 at 11:43

2 Answers2

72

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.

Kirill Rakhman
  • 42,195
  • 18
  • 124
  • 148
  • 11
    it is hard to believe that the compiler couldn't figure the last example out without a real Unit instance. – voddan Jun 16 '16 at 04:43
  • 1
    unfortunately `java.util.function.*` only available above 23. refer this answer [here](https://stackoverflow.com/a/48686292/3763032) and use [android-retrostreams](https://github.com/retrostreams/android-retrostreams) to support API below 24 – mochadwi Jan 02 '20 at 08:16
1

I use this approach for Kotlin & Java. The methods of MyKotlinClass you will see in Java, in Kotlin you will see both methods (class method + extension function).

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) })
}
ultraon
  • 2,220
  • 2
  • 28
  • 27