I understand there are two ways for lazy initialization in kotlin. first by lateinit
which is dynamic but it is only for var
. second, by lazy
delegate which is for val
but it is static, which means it can't be initialized at runtime.
I was wondering is there a way to have lazy dynamic initialization for immutable properties(val
)????
property delegation also works like lazy
and even if we define a custom delegate, its always static initialization. (to my knowledge)
is there a workaround for this? could it be implemented somehow?
so what I wish for, is something like lateinit val
, shown in below code:
class MyClass: SomeCallback {
private lateinit val myData: String
override fun onStatusChanged(status: Status, data: String) {
if(status == Status.DataConfirmed ) {
myData = data
}
}
}
The best I can come up with is a read-write property delegate that throws if you access it before setting it, or if you set it multiple times. Kotlin doesn't let you lateinit
a val
. This is likely because it would be nonsensical to call a setter for a property that doesn't have one. I doubt they want to introduce the can of worms it would be to directly set the value of a backing field from anywhere besides the initializer, because it would be ambiguous.
A delegate like this should be adequate. If it's not adequate to help you immediately fix the bug of calling the setter multiple times, I would say that's a code smell that the class is too complicated and needs to be broken up into smaller units.
class Once<T>: ReadWriteProperty<Any, T> {
private object UNINITIALIZED
private var _value: Any? = UNINITIALIZED
override fun getValue(thisRef: Any, property: KProperty<*>): T {
if (_value !== UNINITIALIZED) {
@Suppress("UNCHECKED_CAST")
return _value as T
}
throw UninitializedPropertyAccessException("Property [$property] was accessed before it was initialized.")
}
override fun setValue(thisRef: Any, property: KProperty<*>, value: T) {
if (_value === UNINITIALIZED) {
_value = value
} else {
error("Cannot set property [$property] more than once.")
}
}
}