Параметр функции Котлина с приемником, вызываемый из Groovy - PullRequest
0 голосов
/ 02 июля 2018

Kotlin и Groovy оба предоставляют способ написать функцию высокого порядка, где параметр функции имеет неявный получатель.

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

Моя цель - написать функцию withReceiver на Kotlin, но вызывать ее из groovy и получить { hello() } work. Однако, как написано, Kotlin генерирует байт-код, такой как

public final void withReceiver(@NotNull Function1 fn) { /* ... */ }

, который Groovy рассматривает как функцию с параметром. Другими словами, чтобы позвонить Котлину withReceiver из Groovy, я должен сделать это:

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

Чтобы разрешить { hello() } без it -> it., я должен добавить перегрузку, которая принимает groovy.lang.Closure в качестве параметра.

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

С учетом этой перегрузки, учитывая экземпляр 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() }

Я пытаюсь сохранить этот синтаксис, но избегайте необходимости писать дополнительную шаблонную перегрузку для каждой функции высокого порядка, которую определяет моя библиотека Kotlin. Есть ли лучший (более простой / автоматический) способ сделать синтаксис функции с приемником Kotlin доступным из Groovy, поэтому мне не нужно вручную добавлять шаблонную перегрузку для каждой из моих функций Kotlin?

Полный код и инструкции по компиляции для моего игрушечного примера выше: на gitlab .

1 Ответ

0 голосов
/ 05 июля 2018

в Groovy вы можете определять новые функции динамически

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

это определит новую функцию withReceiver для класса KotlinVersion

и позволит использовать этот синтаксис для KotlinVersion экземпляра:

kv.withReceiver{ toString() }

в этом случае toString() будет вызываться на kv

вы можете написать функцию, которая перебирает объявленные методы вашего класса kotlin с параметром kotlin.Function и объявлять новый метод, но с параметром groovy.lang.Closure через metaClass.

...