Kotlin and Groovy both provide a way to write a high-order function where the function parameter has an implicit receiver.
Kotlin Version
class KotlinReceiver {
fun hello() {
println("Hello from Kotlin")
}
}
class KotlinVersion {
fun withReceiver(fn: KotlinReceiver.() -> Unit) {
KotlinReceiver().fn()
}
}
// And then I can call...
val foo = KotlinVersion()
foo.withReceiver { hello() }
Groovy Version
class GroovyReceiver {
void hello() {
println("Hello from Groovy")
}
}
class GroovyVersion {
void withReceiver(Closure fn) {
fn.resolveStrategy = Closure.DELEGATE_FIRST
fn.delegate = new GroovyReceiver()
fn.run()
}
}
// And then I can call...
def foo = new GroovyVersion()
foo.withReceiver { hello() }
My goal is to write the withReceiver
function in Kotlin, but call it from groovy and have { hello() }
work. As written, though, Kotlin generates bytecode like
public final void withReceiver(@NotNull Function1 fn) { /* ... */ }
which Groovy treats as a function with a parameter. In other words, to call Kotlin's withReceiver
from Groovy, I have to do this:
(new KotlinVersion()).withReceiver { it -> it.hello() }
In order to allow { hello() }
with no it -> it.
, I have to add an overload that takes a groovy.lang.Closure
as its parameter.
Kotlin Version
import groovy.lang.Closure
class KotlinVersion {
fun withReceiver(fn: KotlinReceiver.() -> Unit) {
KotlinReceiver().fn()
}
fun withReceiver(fn: Closure<Any>) = withReceiver {
fn.delegate = this
fn.resolveStrategy = Closure.DELEGATE_FIRST
fn.run()
}
}
With that overload in place, given a KotlinVersion
instance called foo
the following line works in both languages:
// If this line appears in Groovy code, it calls the Closure overload.
// If it's in Kotlin, it calls the KotlinReceiver.() -> Unit overload.
foo.withReceiver { hello() }
I'm trying to keep that syntax, but avoid having to write that extra boilerplate overload for each high-order function my Kotlin library defines. Is there a better (more seamless/automatic) way of making Kotlin's function-with-receiver syntax usable from Groovy so I don't have to manually add a boilerplate overload to each of my Kotlin functions?
The complete code and compile instructions for my toy example above are on gitlab.