Toggling a nullable value (using setter delegate?)


#1

I’m looking for a concise and reusable way for this construct:

fun select(value: SomeType) {
    data.myValue = if (data.myValue == value) null else value
}

I solved it with the following extension function, but i’m not happy. It pollutes all types and it’s still very verbose:

fun <T> T?.toggleNullable(value: T): T? {
    if (this == value) {
        return null
    } else {
        return value
    }
}

fun select(value: SomeType) {
    data.myValue = data.myValue.toggleNullable(value)
}

Any tips on how to solve this using a generic delegate or something?

Cheers!


#2

You can implement this as a delegated property:

import kotlin.reflect.KProperty

class NullableSetterDelegate<T> {
    private var store: T? = null

    operator fun getValue(thisRef: Any?, property: KProperty<*>): T? = store

    operator fun setValue(thisRef: Any?, property: KProperty<*>, value: T?) {
        if (store == value) {
            store = null
        }
        else {
            store = value
        }
    }
}

class C {
    var foo: String? by NullableSetterDelegate()
}

fun main(args: Array<String>) {
    val c = C()
    c.foo = "abc"
    c.foo = "abc"
    println(c.foo)
}

#3

With 1.1 this can be done a bit more concisely:

operator fun setValue(thisRef: Any?, property: KProperty<*>, value: T?) {
    store = value.takeUnless { it == store }
}