Kotlin отправляет аргументы функции с помощью класса данных

#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> .