I want to create a variable of a certain type that is not null, say Foo for example.
I then want all access to the variable to return Foo, just like a lazy delegate, however, I also want to be able to reset it.
Something like:
var foo : String by Foo(init: {"bar"})
print(foo) // prints "bar"
foo = null // or foo.reset()
print(foo) // prints "bar"
The problem I am trying to solve: I have an index for an adapter that I need to recreate when the adapter content changes. So on change I want to clear the index, and the next time someone tries to accessing it, I want to recreate it.
If the goal is to have a lazy initialized var property
which can be reset to it's initial state you can adapt Kotlin's SynchronizedLazyImpl to allow the invalidate feature:
private object UNINITIALIZED_VALUE
class InvalidatableLazyImpl<T>(private val initializer: () -> T, lock: Any? = null) : Lazy<T>, Serializable {
@Volatile private var _value: Any? = UNINITIALIZED_VALUE
private val lock = lock ?: this
fun invalidate(){
_value = UNINITIALIZED_VALUE
}
override val value: T
get() {
val _v1 = _value
if (_v1 !== UNINITIALIZED_VALUE) {
return _v1 as T
}
return synchronized(lock) {
val _v2 = _value
if (_v2 !== UNINITIALIZED_VALUE) {
_v2 as T
}
else {
val typedValue = initializer()
_value = typedValue
typedValue
}
}
}
override fun isInitialized(): Boolean = _value !== UNINITIALIZED_VALUE
override fun toString(): String = if (isInitialized()) value.toString() else "Lazy value not initialized yet."
operator fun setValue(any: Any, property: KProperty<*>, t: T) {
_value = t
}
}
Which could then be used as follows:
private val fooDelegate = InvalidatableLazyImpl({"bar"})
var foo:String by fooDelegate
println(foo); // -> "bar"
foo = "updated"
println(foo); // -> "updated"
fooDelegate.invalidate()
println(foo); // -> "bar"
One could obviously modify the delegate implementation to allow for null
value to act as a reset however it could make the code harder to reason about i.e:
println(obj.foo); //-> prints "bar
obj.foo = null //reset the value, implicitely
println(obj.foo); //-> prints "bar", but hey didn't I just said `null`
I have the same requirement and wrote LazyEx
class as follows.
Based on the concept of miensol. LazyEx
wraps the original Kotlin's Lazy
property instead of adapting it.
fun <T> lazyEx(initializer: () -> T): LazyEx<T> = LazyEx(initializer)
class LazyEx<out T>(private var initializer: () -> T) : Lazy<T> {
@Volatile
private var wrap = Wrap()
override val value: T get() = wrap.lazy.value
override fun isInitialized() = wrap.lazy.isInitialized()
override fun toString() = wrap.lazy.toString()
fun invalidate() { wrap = Wrap() } // create a new Wrap object
private inner class Wrap { val lazy = lazy(initializer) }
}
usage:
object LazyExTest {
var i = 0
val fooDelegate = LazyEx { "bar${i++}" }
val foo by fooDelegate
fun run() {
println(foo) // -> "bar0"
println(fooDelegate.value) // -> "bar0"
fooDelegate.invalidate()
println(foo) // -> "bar1"
println(fooDelegate.value) // -> "bar1"
fooDelegate.invalidate()
println(fooDelegate.value) // -> "bar2"
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With