Why do I have to return Unit.INSTANCE when implementing in Java a Kotlin function that returns a Unit?

前端 未结 2 764
無奈伤痛
無奈伤痛 2020-12-05 04:26

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:

         


        
相关标签:
2条回答
  • 2020-12-05 04:39

    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) })
    }
    
    0 讨论(0)
  • 2020-12-05 04:51

    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.

    0 讨论(0)
提交回复
热议问题