Как объединить определения и функцию переменного аргумента с получателем в Kotlin - PullRequest
0 голосов
/ 08 сентября 2018

В Kotlin я могу определить функцию, которая принимает переменное число аргументов (см .: testVariableArguments ниже), и я могу определить функцию с указанным получателем (см: testFunctionWithReceiver ниже). Мне интересно, есть ли способ объединить обе эти концепции?

fun main(args: Array<String>) {
    testVariableArguments { a: Int -> println(a) }
    testVariableArguments { a: Int, b: Int -> println("$a, $b") }
    testVariableArguments { a: Int, b: Int, c: Int -> println("$a, $b, $c") }
    testVariableArguments { a: Int, b: Int, c: Int, d: Int -> println("$a, $b, $c, $d") }

    testFunctionWithReceiver {
        doSomething()
        doAnotherThing()
    }
}

fun <R, T : Function<R>> testVariableArguments(function: T) {
    val method = function::class
            .java
            .declaredMethods
            // May need to do something else here to get the
            // correct method in case the return type is 
            // expected to be Object, but for my case it 
            // would never be Object
            .first { m -> m.name == "invoke" && m.returnType != Object::class.java }


    val args = method
            .parameterTypes
            // Mapping to Int here for demonstration, in real
            // situations would use the parameter types to
            // create the correct value
            .withIndex()
            .map { i -> i.index }
            // Not really needed, but would be if I were 
            // using multiple types and not just Int
            .map { i -> i as Any }
            .toTypedArray()

    method.invoke(function, *args)
}

fun <R> testFunctionWithReceiver(function: MyInterface.() -> R) {
    val myObject = object : MyInterface {
        override fun doSomething() {
            println("doing something")
        }

        override fun doAnotherThing() {
            println("doing another thing")
        }
    }
    function(myObject)
}

interface MyInterface {
    fun doSomething()
    fun doAnotherThing()
}

EDIT:

Я нашел способ объединить эти две функции, но он немного запутан на сайте вызовов, поэтому, если есть лучший подход, мне будет интересно.

Я добавил следующую операторную функцию к MyInterface

operator fun <R, T : Function<R>> T.unaryPlus() {
    testVariableArgumentDefinition(this)
}

Затем, когда я звоню testFunctionWithReceiver, я делаю следующее:

testFunctionWithReceiver {
    +{ a: Int, b: Int ->
        println("$a, $b")
        doSomething()
        doAnotherThing()
    }
}

1 Ответ

0 голосов
/ 06 ноября 2018

Вы можете добавить требования к параметру в замыкание, вызываемое получателем, добавив:

MyInterface.(P) -> R

Замена созданного вами тестового параметра на фиксированный, вот как это может выглядеть:

fun <R, P> testVariableArgumentsWithReceiver(param: P, function: MyInterface.(P) -> R) {
    function.invoke(myObject, param)
}

fun main(args: Array<String>) {
    testVariableArgumentsWithReceiver(17) { a: Int ->
        println("$a")
        doSomething()
        doAnotherThing()
    }
}

Конечно, вы здесь не так гибки, как вам нужно передать одно значение типа P (которое может быть массивом). Вы можете расширить его до MyInterface.(P,Q), но не до произвольных подписей как таковых.

Что вы действительно хотите, так это подпись:

fun <R, T: Function<R>> someName(function: MyInterface.T) 

или

fun <R, T: FunctionWithReceiver<MyInterface, R>> someName(function: T) 

Насколько мне известно, ни то, ни другое в настоящее время не может быть выражено:

  • Только типы функций литералы разрешены как часть типов расширений; MyInterface.T недействительный код.
  • Кажется, не существует первоклассного типа для функций с приемником; мы не можем объявить FunctionWithReceiver.

Возможно, стоит поднять вопрос на обсуждения.kotlinlang.org

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...