带接收器的 Kotlin 函数参数,从 Groovy 调用

Dan*_*Dan 6 groovy kotlin kotlin-interop

Kotlin 和 Groovy 都提供了一种编写高阶函数的方法,其中函数参数具有隐式接收器。

科特林版本

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() }
Run Code Online (Sandbox Code Playgroud)

Groovy 版本

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() }
Run Code Online (Sandbox Code Playgroud)

withReceiver我的目标是用 Kotlin编写该函数,但从 groovy 调用它并进行{ hello() }工作。不过,正如所写,Kotlin 生成的字节码如下

public final void withReceiver(@NotNull Function1 fn) { /* ... */ }
Run Code Online (Sandbox Code Playgroud)

Groovy 将其视为带有参数的函数。换句话说,要从withReceiverGroovy 调用 Kotlin,我必须这样做:

(new KotlinVersion()).withReceiver { it -> it.hello() }
Run Code Online (Sandbox Code Playgroud)

为了允许{ hello() }no it -> it.,我必须添加一个以 agroovy.lang.Closure作为参数的重载。

科特林版本

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()
    }
}
Run Code Online (Sandbox Code Playgroud)

完成该重载后,给定一个名为以下行的KotlinVersion实例可以在两种语言中工作:foo

// 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() }
Run Code Online (Sandbox Code Playgroud)

我试图保留该语法,但避免为我的 Kotlin 库定义的每个高阶函数编写额外的样板重载。是否有更好(更无缝/自动)的方法使 Kotlin 的带有接收器的函数语法可以从 Groovy 中使用,这样我就不必手动向每个 Kotlin 函数添加样板重载?

上面我的玩具示例的完整代码和编译说明位于gitlab 上

dag*_*ett 2

在groovy中你可以动态定义新函数

KotlinVersion.metaClass.withReceiver = { Closure c-> 
    delegate.with(c) 
}
Run Code Online (Sandbox Code Playgroud)

withReceiver这将为类定义新函数KotlinVersion

并允许使用此语法来KotlinVersion实例:

kv.withReceiver{ toString() }
Run Code Online (Sandbox Code Playgroud)

在这种情况下toString()将被调用kv

您可以编写一个函数,该函数使用参数迭代 kotlin 类的已声明方法kotlin.Function,并通过元类声明新方法但使用groovy.lang.Closure参数。