Lambda with Receiver Java Interop

In Kotlin we have lambdas with receivers which make creating small DSLs rather easy. However, the Java Interop seems to not be as great and there doesn’t seem to be any alternatives short of duplicating everything to include a Java friendly option.

Given this simple example:

fun withReceiver(receiver: String.() -> Unit) {
    "".receiver()
}

I can use this in Kotlin, very straight forward:

withReceiver {
    toLowerCase()
}

(Setting aside that this code doesn’t really do much)

This however gets compiled to something that requires returning Unit in Java:

WithReceiverKt.withReceiver(string -> {
    string.toLowerCase();
    return Unit.INSTANCE;
});

For lambdas that don’t use a receiver, this can be easily fixed by using a functional interface, here Consumer<String> would work.

But for lambdas with a receiver this is not an option as you’d lose the receiver when using it from Kotlin. There is the “sam-with-receiver” compiler plugin, but from my testing this is required at the usage location, not at the definition. In this case, the Kotlin code using withReceiver would have to apply that plugin. As the library author of withReceiver, I’d like to avoid having to push something like that onto my users.

There’s an issue on the Kotlin issue tracker to add @JvmVoid but that issue seems to be going nowhere.

To sum up: Is there anything I can do to allow calling Kotlin code to use a lambda with receiver and Java code to not require returning Unit that doesn’t involve creating two definitions of withReceiver?

1 Like

This should work, but sadly it has a bit of boilerplate :frowning: :

fun interface StringConsumer {
    fun String.consume()
}

fun StringConsumer.consume(str: String) = str.consume()

fun withString(consumer: StringConsumer){
	val consumer: String.() -> Unit = consumer::consume
    
    "Hello, world!".consumer()
}

fun main(){
    withString {
        println(this)
    }
}