Is there a way to tell if a lazy val has been initialised in Kotlin without initialising it in the process?
eg if I have a lazy val, querying if it is null would instantiate it
val messageBroker: MessageBroker by lazy { MessageBroker() }
if (messageBroker == null) {
// oops
}
I could potentially use a second variable, but that seems messy.
private var isMessageBrokerInstantiated: Boolean = false
val messageBroker: MessageBroker by lazy {
isMessageBrokerInstantiated = true
MessageBroker()
}
...
if (!isMessageBrokerInstantiated) {
// use case
}
Is there some sexy way of determining this, like if (Lazy(messageBroker).isInstantiated())
?
Related (but not the same): How to check if a "lateinit" variable has been initialized?
There is a way, but you have to access the delegate object which is returned by lazy {}
:
val messageBrokerDelegate = lazy { MessageBroker() }
val messageBroker by messageBrokerDelegate
if(messageBrokerDelegate.isInitialized())
...
isInitialized
is a public method on interface Lazy<T>
, here are the docs.
Since Kotlin 1.1, you can access a property delegate directly using .getDelegate()
.
You can write an extension property for a property reference that checks that it has a Lazy
delegate that has already been initialized:
/**
* Returns true if a lazy property reference has been initialized, or if the property is not lazy.
*/
val KProperty0<*>.isLazyInitialized: Boolean
get() {
if (this !is Lazy<*>) return true
// Prevent IllegalAccessException from JVM access check on private properties.
val originalAccessLevel = isAccessible
isAccessible = true
val isLazyInitialized = (getDelegate() as Lazy<*>).isInitialized()
// Reset access level.
isAccessible = originalAccessLevel
return isLazyInitialized
}
Then at the use site:
val messageBroker: MessageBroker by lazy { MessageBroker() }
if (this::messageBroker.isLazyInitialized) {
// ... do stuff here
}
This solution requires kotlin-reflect
to be on the classpath. With Gradle, use
compile "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
The isAccessible = true
part is required for the .getDelegate()
, because otherwise it cannot access the private field storing the delegate reference.
Building on hotkey's solution, you can make an isLazyInitialized property (instead of a function), to be consistent with the isInitialized property for lateinit vars.
Also, there isn't any need to handle the null case.
import kotlin.reflect.KProperty0,
import kotlin.reflect.jvm.isAccessible
val KProperty0<*>.isLazyInitialized: Boolean
get() {
// Prevent IllegalAccessException from JVM access check
isAccessible = true
return (getDelegate() as Lazy<*>).isInitialized()
}