#function #kotlin #arguments #parameter-passing #data-class
Вопрос:
Допустим, у меня занятия:
class Foo {
fun doSomething(param1: Int, param2: String, param3: String)
}
и класс данных
data class Params(
val param1: Int,
val param2: String,
val param3: String)
Теперь я хочу использовать аргументы класса данных для отправки их в функцию, есть ли способ сделать это? Давайте скажем что-то похожее на:
val myParams = Params(1, "2", "3")
val foo = Foo()
foo.doSomething(myparams)
Или с помощью какого-либо преобразования или именования метода. как:
execute(foo, foo::doSomething, myParams)
Комментарии:
1. Я не вижу другого способа, кроме отражения или генерации кода. Так что, вообще говоря: нет, это невозможно в Котлине без некоторых трюков. Лучшее, что вы можете сделать, — это перегрузить
doSomething()
, чтобы принятьParams
или создать функцию расширения.2. Да, я не хочу перегружать, так как это разрушит цель, которую я ищу. Как бы это было с отражением?
Ответ №1:
Я сомневаюсь, что это возможно в Котлине без некоторых трюков. Возможными решениями являются API отражения и генерация кода.
Пример использования отражения:
fun main() {
val myParams = Params(1, "2", "3")
val foo = Foo()
invokeWithParams(foo::doSomething, myParams)
}
fun <T : Any, R> invokeWithParams(func: KFunction<R>, params: T): R {
val paramValues = func.parameters.map { kparam ->
(params::class as KClass<T>)
.memberProperties
.single { it.name == kparam.name }
.get(params)
}.toTypedArray()
return func.call(*paramValues)
}
Он должен работать для статических функций, функций-членов и функций расширения. Это может не сработать в некоторых более редких случаях. Вероятно, вам следует добавить некоторую обработку ошибок, например, проверку соответствия параметров.
Это не будет работать ни на чем другом, кроме JVM, поскольку отражение по-прежнему очень ограничено для других целей.
Кроме того, я не совсем уверен в этом небезопасном актерском составе. Я думаю, что это не может не сработать, но я не уверен в этом на 100%.
Обновить:
Мы можем сделать это немного забавнее, преобразовав функцию в расширение operator invoke
:
operator fun <T : Any, R> KFunction<R>.invoke(params: T): R
Тогда мы можем использовать его с любой функцией, подобной этой:
(foo::doSomething)(myParams)
Я не уверен, что это хорошая идея, хотя, поскольку это более запутанно, чем явный вызов функции утилиты.
Комментарии:
1. Приятно, я пробовал что-то подобное, но у меня были проблемы с дженериками, я скучал
as KClass<T>
2. Да, в Kotlin
dog::class
возвращаетKClass<out Dog>
значение, означающее, что мы действительно не можем использовать его для доступа к каким-либо членамdog
. Я считаю, что причина в том, что в противном случае можно было бы:(dog as Animal)::class
а затем использовать его для доступаDog
к членам сAnimal
объектом. Но если мы уверены, что используем один и тот жеdog
объект как для приобретенияKClass
, так и для использования его членов, я думаю, что это безопасноKClass<out Dog>
KClass<Dog>
.