Reputation: 423
I use a property observable delegate.
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()
}
So, I use this infix function to make pretty look of comparing two values.
But if I want to make a library out of it, I need to move this infix function somewhere so there is no need to define it every time. But I can't figure out the way because it depends on two concrete values old
and new
. So I want it to look like this:
var state: State by Delegates.observable(START as State,
fun(prop: KProperty<*>, old: State, new: State) {
when {
START into STOP -> {
doSomeMagic()
}
And define into
somewhere else.
Upvotes: 0
Views: 504
Reputation: 85946
This is possible but takes a little work and a bit of a structural change to how the observable delegate works.
First, create a class to hold the state on a change, this will allow you to also add your infix function to this 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
}
}
Now create a new delegate function that will create the delegate and instead of calling a lambda function with all the values as a parameter, will expect a lambda that is an extension method on the StateChange
class. Therefore this lambda will have access to its properties and functions as well.
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() }
}
}
Now use it anywhere you want and your infix function will be available:
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
}
}
}
Note a slightly different syntax than you are using for passing in the lambda function to the observableState
function, this is more idiomatic to not declare the full function header and instead just have the lambda body with everything inferred. Now there are no parameters anyway.
The cost of this is the new allocation of the small data class each time an event is fired.
Upvotes: 3