Kotlin lazy default property

2019-03-25 16:26发布

问题:

In Kotlin, how do i define a var that has a lazy default value ?

for example, a val would be something like this:

val toolbarColor  by lazy {color(R.color.colorPrimary)}

What i want to do is, have a default value for some property (toolbarColor), and i can change that value for anything else. Is it possible?

EDIT: This does the partial trick.

var toolbarColor = R.color.colorPrimary
    get() = color(field)
    set(value){
        field = value
    }

Is it possible to ease this by writing

var toolbarColor = color(R.color.colorPrimary)
    set(value){
        field = value
    }

in a way that the default value is computed lazily? At the moment it won't work because color() needs a Context that is only initialized later.

回答1:

You can create your own delegate method:

private class ColorDelegate<T>(initializer: () -> T) : ReadWriteProperty<Any?, T> {

    private var initializer: (() -> T)? = initializer

    private var value: T? = null

    override fun getValue(thisRef: Any?, property: KProperty<*>): T {
        return value ?: initializer!!()
    }

    override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
        this.value = value
    }
}

Declare in some delegate:

object DelegatesExt {
    fun <T> lazyColor(initializer: () -> T): ReadWriteProperty<Any?, T> = ColorDelegate(initializer)
}

And use as follow:

var toolbarColor by DelegatesExt.lazyColor {
    // you can have access to your current context here.
    // return the default color to be used
    resources.getColor(R.color.your_color)
}

...

override fun onCreate(savedInstanceState: Bundle?) {
    // some fun code
    // toolbarColor at this point will be R.color.your_color
    // but you can set it a new value
    toolbarColor = resources.getColor(R.color.new_color)
    // now toolbarColor has the new value that you provide.
}

I think this could be a cleaner way to do, but I don't know yet (starting with kotlin few days ago). I will take a look and see if this could be done with less code.



回答2:

You can store your property in a map to basically create a mutable lazy. You need a mutable map (like a HashMap<K, V>) with a default function to delegate to:

var toolbarColor by hashMapOf<String, Any?>()
        .withDefault { toolbarColor = R.color.colorPrimary; toolbarColor }

You'll also need to import some extension functions: import kotlin.properties.getValue and import kotlin.properties.setValue.

It would be nice if Kotlin provided something built-in and optimized for this (like a mutableLazy or something). As such, I've created KT-10451.