#kotlin #generics #lambda #delegation #side-effects
#kotlin #дженерики #лямбда #делегирование #побочные эффекты
Вопрос:
Как я могу написать универсальную функцию Kotlin, которая принимает функцию в качестве аргумента и добавляет к ней побочный эффект? Например,
fun something(one: Int, two: String): String { return "${one}, ${two}" }
fun somethingElse(arg: Array<String>): String { return "${arg}" }
val w1 = wrapped(::something)
w1(42, "hello")
val w2 = wrapped(::somethingElse)
w2(arrayOf("ichi", "ni"))
Следующее работает для функций, которые принимают только один параметр:
fun <A, R> wrapped(theFun: (a: A) -> R): (a: A) -> R {
return { a: A ->
theFun(a).also { println("wrapped: result is $it") }
}
}
Чтобы заставить это работать с произвольным количеством аргументов, мне понадобится некоторая конструкция, которая дает мне тип списка аргументов. К сожалению, функция generic не может быть использована, поскольку она принимает только один параметр. Следующее не компилируется:
fun <A, R> wrapped(theFun: Function<A, R>): Function<A, R> {
return { args: A ->
theFun(*args).also { println("wrapped: result is ${it}") }
}
}
Или, может быть, я мог бы использовать varargs
? Похоже, не работает с лямбдами. Или отражение Kotlin?
Комментарии:
1. Возможно только с отражением.
Ответ №1:
Решение с использованием отражения:
class KFunctionWithSideEffect<R>(private val f: KFunction<R>, private val sideEffect: (R) -> Unit) : KFunction<R> by f {
override fun call(vararg args: Any?) = f.call(*args).also { sideEffect(it) }
override fun callBy(args: Map<KParameter, Any?>) = f.callBy(args).also { sideEffect(it) }
}
fun <R> wrapped(theFun: KFunction<R>, sideEffect: (R) -> Unit = { str -> println("wrapped: result is $str") }) =
KFunctionWithSideEffect(theFun, sideEffect)
Использование:
val w1 = wrapped(::something)
w1.call(42, "hello")
val w2 = wrapped(::somethingElse)
w2.call(arrayOf("ichi", "ni"))
Комментарии:
1. Спасибо, кажется, работает. Хотя это выглядит довольно ужасно. Немного грустно, что такого рода вещи очень легко сделать в TypeScript, но не в Kotlin.