Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to declare a Kotlin Lambda with return type 'void' for a java caller?

I have a library that is completely written in Kotlin including its public API. Now a user of the library uses Java, the problem here is that Kotlin Lambdas with return type Unit are not compiled to return type void. The effect is that the Java side has always to return Unit.INSTANCE for methods that are effectivly void. Can this be avoided somehow?

Example:

Kotlin Lambda

interface Foo{
  fun bar(x:(String)->Unit)
}

Java call

public void call(){
   foo.bar(this::processString)
}

//the return type should rather be void instead of Unit
public Unit processString(String s){  
    return Unit.INSTANCE 
    // ^^ implementations should not be forced to return anything 
 }

Is it possible to declare the Kotlin Lambda differently so the compiler generates a void return type?

see also How to declare a Kotlin function with return type 'void' for a java caller?

like image 793
Chriss Avatar asked Dec 06 '18 14:12

Chriss


People also ask

What is the return type of lambda expression void?

These are for single–line lambda expressions having void return type. Type 1: No Parameter. Type 2: Single Parameter. The type and return type of the lambdas are automatically inferred.

How do I return in lambda Kotlin?

Just use the qualified return syntax: return@fetchUpcomingTrips . In Kotlin, return inside a lambda means return from the innermost nesting fun (ignoring lambdas), and it is not allowed in lambdas that are not inlined. The return@label syntax is used to specify the scope to return from.

Can lambda have return statement in Java?

In order to do more complex operations, a code block can be used with curly braces. If the lambda expression needs to return a value, then the code block should have a return statement.

How do you write a void function in Kotlin?

By default, Java void is mapped to Unit type in Kotlin. This means that any method that returns void in Java when called from Kotlin will return Unit — for example the System. out. println() function.


1 Answers

If Java interop is of topmost priority I would either just use the Java functional interfaces directly (i.e. Consumer, Supplier, etc.) or create custom Kotlin functional interfaces for now. Meanwhile Kotlin handles functional interfaces better...

Java variant:

interface Foo{
  fun bar(x : java.util.function.Consumer<String>)
}
// calling this from Kotlin today looks the same as if we used (String) -> Unit:
foo.bar { println(it) }

Kotlin variant with custom Consumer:

fun interface MyConsumer<T> { // just a demo... probably depends on your needs
   fun accept(t : T)
   // other functions?
}

Usage is similar as above. Maybe there is even an easier way today to handle something like (String) -> Unit as Consumer<String> but then I do not know it yet (or didn't feel the need to research for it yet ;-)). Compiler annotations as Ilya mentioned in the comments may be a way to solve this issue ~centrally.

In Dec 2018 I wrote: I do not have a real answer to this, but I will share, what I did in such a situation where I needed to access such Kotlin code from Java (or what has come to my mind).

Basically it depends which side you really want to touch/enhance just to get what you require.

Enhancing the Kotlin code to support the Java equivalents:

interface Foo {
  fun bar(x : (String) -> Unit)

  /* the following is only here for Java */
  @JvmDefault // this requires that you add -Xjvm-default=enable to your compiler flags!
  fun bar(x:Consumer<String>) = bar(x::accept)
}

This has some drawbacks: the Consumer-method is visible from Kotlin as well and is therefore also callable from there. Needless to say, that you need to duplicate all the functions in the interfaces and therefore your whole Kotlin-interfaces just get more bloated. But: it works from both sides the way you would expect. Java calls the Consumer-variant, Kotlin calls the (String) -> Unit-variant... hopefully ;-) actually just demoing some calls:

// from Java:
..bar(s -> { System.out.println(s); })
// however, method references might not work that easily or not without a workaround...
..bar((Consumer<String>) System.out::println); // not nice... @JvmName("kotlinsBar") to the rescue? well... that will just get more and more ugly ;-)

// from Kotlin:
..bar(Consumer(::println)) // or: ..bar(Consumer { println(it) })
..bar(::println)           // or: ..bar { println(it) } // whatever you prefer...

That having said, another variant is to add helper methods that actually help calling the Kotlin functions easier from Java, e.g. something as follows:

fun <T> `$`(consumer: Consumer<T>): (T) -> Unit = consumer::accept

Which will probably never be called from Kotlin (as writing the backticks combined with the $ is already cumbersome enough) or if you do not want to bloat your Kotlin code just add such a method to Java, where however it doesn't look that slim:

static <T> Function1<T, Unit> $(Consumer<T> consumer) {
    return t -> {
        consumer.accept(t);
        return Unit.INSTANCE;
    };
}

Calls to those methods both look the same:

..bar($(s -> /* do something with s */)) // where bar(x : (String) -> Unit)

For the things I needed to solve I just returned Unit.INSTANCE or null, but if I had more methods to call I would probably have chosen the second ($(...)) approach. In the best case I only need to supply (generate? ;-)) equivalents once and use them in several projects whereas supplying default variants in the interfaces just for Java will probably require way more work and may even confuse some people...

Finally: no... I don't know of any option that allows you to have something like void-functional interfaces (/consumers) out of the Unit-returning functional interfaces of Kotlin.

like image 96
Roland Avatar answered Sep 28 '22 04:09

Roland