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

时间:2018-07-02 16:15:23

标签: 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() }

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() }

我的目标是在Kotlin中编写withReceiver函数,但从常规上调用它并运行{ hello() }。不过,按照编写的方式,Kotlin会生成类似的字节码

public final void withReceiver(@NotNull Function1 fn) { /* ... */ }
Groovy将其视为具有参数的函数的

。换句话说,要从Groovy调用Kotlin的withReceiver,我必须这样做:

(new KotlinVersion()).withReceiver { it -> it.hello() }

为了允许{ hello() }不包含it -> it.,我必须添加一个以groovy.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()
    }
}

有了适当的重载,给定名为KotlinVersion的{​​{1}}实例,以下行就可以在两种语言中工作:

foo

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

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

1 个答案:

答案 0 :(得分:1)

在Groovy中,您可以动态定义新功能

KotlinVersion.metaClass.withReceiver = { Closure c-> 
    delegate.with(c) 
}

这将为类withReceiver定义新功能KotlinVersion

,并将允许将此语法用于KotlinVersion实例:

kv.withReceiver{ toString() }

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

您可以编写使用kotlin.Function参数通过kotlin类的声明方法进行迭代的函数,并通过metaClass使用groovy.lang.Closure参数声明新方法。