【问题标题】:Kotlin function parameter with receiver, called from Groovy带有接收器的 Kotlin 函数参数,从 Groovy 调用
【发布时间】:2018-07-02 16:15:22
【问题描述】:

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

Kotlin 版本

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 函数,但从 groovy 调用它并让 { 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 作为参数的重载。

Kotlin 版本

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

有了这个重载,给定一个名为fooKotlinVersion 实例,以下行在两种语言中都适用:

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

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

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

【问题讨论】:

  • 极限在哪里?为什么不能创建字节码中的 kotlin 代码:public final void withKotlinReceiver(groovy.lang.Closure fn) { /* ... */ }
  • @daggett 我的解决方法中的 Kotlin 示例正是这样做的,我只是想避免为我的库中的每个高阶函数手动编写这样的包装器。我认为可能有一些 Groovy 魔法和/或 Kotlin 编译器属性的组合可以在没有大量手写样板的情况下实现相同的语法。
  • 请提供更多详细信息,您如何看待 groovy 脚本/类以及它如何导入 kotlin..
  • 我不确定你在问什么,所以我编辑了这个问题,试图让我想要达到的目标更清楚。上下文是我的 Kotlin 代码位于 buildSrc 文件夹中,我试图让它可以从用 Groovy 编写的 gradle 脚本中调用。我检查了玩具示例,带有编译说明,into gitlab

标签: groovy kotlin kotlin-interop


【解决方案1】:

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

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

这将为类KotlinVersion定义新函数withReceiver

并且将允许对KotlinVersion 实例使用此语法:

kv.withReceiver{ toString() }

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

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

【讨论】:

    猜你喜欢
    • 2020-03-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-02-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多