Подтвердить что ты не робот

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" переменная была инициализирована?

4b9b3361

Ответ 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()
    }