Kotlin - 将中缀函数从其他函数中移出

Kotlin - Move infix function inside other function out of it

我使用 属性 可观察委托。

var state: State by Delegates.observable(START as State,
          fun(prop: KProperty<*>, old: State, new: State) {
                  infix fun State.into(s: State): Boolean {
                      return this == old && s == new
                  }

                  when {
                      START into STOP -> {
                          doSomeMagic()
                  }

所以,我使用这个中缀函数来比较两个值。 但是如果我想用它做一个库,我需要把这个中缀函数移到某个地方,这样就不需要每次都定义它了。但我想不出办法,因为它取决于两个具体值 oldnew。所以我希望它看起来像这样:

var state: State by Delegates.observable(START as State,
          fun(prop: KProperty<*>, old: State, new: State) {
                  when {
                      START into STOP -> {
                          doSomeMagic()
                  }

并在其他地方定义 into

这是可能的,但需要做一些工作,并对可观察委托的工作方式进行一些结构更改。

首先,创建一个 class 来保持状态发生变化,这将允许您将中缀函数添加到此 class:

data class StateChange<T>(val property: KProperty<*>, val oldValue: T, val newValue: T) {
    infix fun State.into(s: State): Boolean {
        return this == oldValue && s == newValue
    }
}

现在创建一个新的委托函数,它将创建委托而不是调用一个 lambda 函数,将所有值作为参数,将期望一个 lambda 是 StateChange [=27 上的扩展方法=].因此,此 lambda 也可以访问其属性和函数。

inline fun <T> observableState(initialValue: T, crossinline onChange: StateChange<T>.() -> Unit):
        ReadWriteProperty<Any?, T> =
        object : ObservableProperty<T>(initialValue) {
            override fun afterChange(property: KProperty<*>, oldValue: T, newValue: T) {
                with (StateChange(property, oldValue, newValue)) { onChange() }
            }
        }

现在你可以在任何地方使用它,你的中缀函数就可用了:

var state: State by observableState(START) {
    // property, oldValue, and newValue are all here on this object!
    when {
        START into STOP -> {        // works!
            doSomeMagic(newValue)   // example accessing the newValue
        }
    }
}

请注意,与将 lambda 函数传递给 observableState 函数时使用的语法略有不同,这更符合惯用的做法是不声明完整的函数头,而只包含推断出所有内容的 lambda 主体.现在反正没有参数了。

这样做的成本是每次触发事件时新分配的小数据class。