Kotlin: проверьте, был ли инициализирован ленивый val
Есть ли способ узнать, был ли инициализирован ленивый val в Kotlin, не инициализируя его в процессе?
Например, если у меня есть ленивый val, запрос, если он равен null, будет его экземпляр
val messageBroker: MessageBroker by lazy { MessageBroker() }
if (messageBroker == null) {
// oops
}
Я могу потенциально использовать вторую переменную, но это кажется беспорядочным.
private var isMessageBrokerInstantiated: Boolean = false
val messageBroker: MessageBroker by lazy {
isMessageBrokerInstantiated = true
MessageBroker()
}
...
if (!isMessageBrokerInstantiated) {
// use case
}
Есть ли какой-то сексуальный способ определения этого, например if (Lazy(messageBroker).isInstantiated())
?
Связанный (но не тот же): Как проверить, является ли "lateinit" переменная была инициализирована?
Ответы
Ответ 1
Есть способ, но вам нужно получить доступ к объекту делегата, который возвращается lazy {}
:
val messageBrokerDelegate = lazy { MessageBroker() }
val messageBroker by messageBrokerDelegate
if(messageBrokerDelegate.isInitialized())
...
isInitialized
является общедоступным методом интерфейса Lazy<T>
, здесь docs.
Ответ 2
Начиная с Kotlin 1.1, вы можете получить доступ к делегату свойства напрямую, используя .getDelegate()
.
Вы можете написать свойство расширения для ссылки на свойство, которое проверяет наличие у него Lazy
делегата, который уже был инициализирован:
/**
* 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
}
Тогда на сайте использования:
val messageBroker: MessageBroker by lazy { MessageBroker() }
if (this::messageBroker.isLazyInitialized) {
// ... do stuff here
}
Это решение требует, чтобы kotlin-reflect
был на пути к классам.С Gradle, используйте
compile "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
Часть isAccessible = true
требуется для .getDelegate()
, поскольку в противном случае она не может получить доступ к закрытому полю, в котором хранится ссылка на делегат.
Ответ 3
Основываясь на решении горячих клавиш, вы можете создать свойство isLazyInitialized (вместо функции), чтобы оно соответствовало свойству isInitialized для переменных lateinit.
Кроме того, нет необходимости обрабатывать нулевой регистр.
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()
}