I’ve been exploring Kotlin’s lazy functionality and realized it’s primarily designed for immutable variables. However, I needed a similar mechanism for mutable properties. As a result, I devised a mutableLazy delegate pattern that aims to support mutability while preserving thread safety through double-checked locking. Here’s the implementation:
import kotlin.properties.ReadWriteProperty import kotlin.reflect.KProperty fun <T> mutableLazy(initializer: () -> T): MutableLazy<T> { return MutableLazy(initializer) } class MutableLazy<T>(initializer: () -> T) : ReadWriteProperty<Any?, T> { companion object { private object UNINITIALIZED } u/Volatile private var value: Any? = UNINITIALIZED private var initializer: (() -> T)? = initializer private val lock = Any() override fun getValue(thisRef: Any?, property: KProperty<*>): T { val _v1 = value if (_v1 != UNINITIALIZED) { u/Suppress("UNCHECKED_CAST") return _v1 as T } return synchronized(lock) { val _v2 = value if (_v2 != UNINITIALIZED) { u/Suppress("UNCHECKED_CAST") _v2 as T } else { val typedValue = initializer!!() value = typedValue initializer = null // Clear the initializer to allow garbage collection of the lambda typedValue } } } override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) { synchronized(lock) { this.value = value } } }
Usage example:
object XXXOptions { private const val THEME = "THEME" val theme: Theme by mutableLazy { val sharedPreferences = XXXApplication.instance.getSharedPreferences() val moshi = Moshi.Builder().build() val jsonTheme = sharedPreferences.getString(THEME, null) if (!jsonTheme.isNullOrBlank()) { moshi.adapter(Theme::class.java).fromJson(jsonTheme)?.let { return@mutableLazy it } } return@mutableLazy PREFERRED_THEME } ... }
I’m reaching out for insights on two aspects:
- Design Review: Is the implementation of MutableLazy, particularly the use of double-checked locking for ensuring thread safety, correctly designed? Are there any potential pitfalls or improvements that could be suggested?
- Companion Object Necessity: The UNINITIALIZED value is encapsulated within a companion object to signify its unique state. However, given that UNINITIALIZED is already a singleton, is there a strong justification for using a companion object? Could it be omitted, or is there a better practice for handling such a scenario?
I appreciate any feedback or alternative approaches that could enhance this implementation. Thank you!
Thank you.
submitted by /u/yccheok
[link] [comments]