Brettins
Brettins

Reputation: 952

How do I make a delegate apply to all properties in a class?

I have a class, A, that needs to be marked as dirty anytime one of its properties is changed.

After reviewing the Kotlin docs, I know I need a delegate. So far I have:


abstract class CanBeDirty {
    var isDirty = false
}
class A(
// properties getting set in constructor here
) : CanBeDirty {
var property1: String by DirtyDelegate()
var property2: Int by DirtyDelegate()
var property3: CustomObject by DirtyDelegate()

}
class DirtyDelegate() {
    operator fun getValue(thisRef: CanBeDirty, property: KProperty<*>): Resource {
        return valueOfTheProperty
    }
    operator fun setValue(thisRef: CanBeDirty, property: KProperty<*>, value: Any?) {
        if (property != value) {
            thisRef.isDirty = true
            //set the value
        }
        else {
            //don't set the value
        }
    }

}

I believe the lack of setting has something to do with vetoable() but the examples I see in Kotlin documentation don't really show me how to do this with a fully formed class Delegate (and I'm just not that up to speed on Kotlin syntax, honestly).

Upvotes: 0

Views: 403

Answers (1)

Tenfour04
Tenfour04

Reputation: 93609

Your delegate class needs its own property to store the value it will return. And if you don't want to deal with uninitialized values, it should also have a constructor parameter for the initial value. You don't have to implement ReadWriteProperty, but it allows the IDE to autogenerate the correct signature for the two operator functions.

class DirtyDelegate<T>(initialValue: T): ReadWriteProperty<CanBeDirty, T> {
    private var _value = initialValue

    override fun getValue(thisRef: CanBeDirty, property: KProperty<*>): T {
        return _value
    }

    override fun setValue(thisRef: CanBeDirty, property: KProperty<*>, value: T) {
        if (_value != value) {
            _value = value
            thisRef.isDirty = true
        }
    }
}

Since this takes an initial value parameter, you have to pass it to the constructor:

class A: CanBeDirty() {
    var property1: String by DirtyDelegate("")
    var property2: Int by DirtyDelegate(0)
    var property3: CustomObject by DirtyDelegate(CustomObject())
}

If you wanted to set an initial value based on something passed to the constructor, you could do:

class B(initialName: String): CanBeDirty() {
    var name by DirtyDelegate(initialName)
}

Upvotes: 3

Related Questions