如何在Kotlin中将变量参数定义和函数与接收器结合

时间:2018-09-08 00:44:15

标签: kotlin

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

编辑:

我已经找到了一种将这两种功能结合在一起的方法,但是在呼叫站点上有点混乱,因此,如果有更好的方法,我会很感兴趣。

我所做的是将以下运算符添加到MyInterface

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

然后,当我致电testFunctionWithReceiver时,我将执行以下操作:

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

1 个答案:

答案 0 :(得分:0)

您可以通过添加以下内容来将参数要求添加到用接收器调用的闭包中:

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) 

据我所知,目前都无法表达:

  • 似乎只允许将功能类型 literals 作为扩展类型的一部分; MyInterface.T是无效代码。
  • 对于带有接收器的函数,似乎没有一流的类型。我们无法声明FunctionWithReceiver

这可能值得提出discuss.kotlinlang.org