【问题标题】:Add a side-effect to a function in a generic way以通用方式向函数添加副作用
【发布时间】:2021-02-14 04:16:49
【问题描述】:

如何编写一个 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") }
    }
}

要使用任意数量的参数,我需要一些结构来提供参数列表的类型。不幸的是,不能使用 Function 泛型,因为它只需要一个参数。以下不编译:

fun <A, R> wrapped(theFun: Function<A, R>): Function<A, R> {
    return { args: A ->
        theFun(*args).also { println("wrapped: result is ${it}") }
    }
}

或者我可以使用varargs?似乎不适用于 lambda。还是 Kotlin 反射?

【问题讨论】:

  • 只能通过反射实现。
  • 老兄,请为接受的答案投票!

标签: kotlin generics lambda delegation side-effects


【解决方案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"))

【讨论】:

  • 谢谢,似乎有效。虽然看起来很恐怖。有点遗憾,这种事情在 TypeScript 中很容易完成,但在 Kotlin 中却不是。
猜你喜欢
  • 2014-04-18
  • 2015-03-01
  • 2011-07-19
  • 2017-08-30
  • 1970-01-01
  • 2021-04-19
  • 1970-01-01
  • 2016-07-22
  • 1970-01-01
相关资源
最近更新 更多