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