Как назначить свойство лениво или обычно условным способом

#android #kotlin #android-workmanager

Вопрос:

Я хотел бы присвоить одно свойство либо лениво, либо «обычным способом», но проблема в том, что мое значение всегда равно «Любому». Я не могу использовать ключевое слово «by», когда я назначаю свойство условно. Вот мой нынешний подход

 abstract class IWorkerContract(private val isLazy: Boolean = false) {
    private val workRequest = if (isLazy) {
       // Type mismatch. Required: OneTimeWorkRequest Found: Lazy<OneTimeWorkRequest>
       lazy {
          OneTimeWorkRequestBuilder<Worker>.build()
       }
    } else {
      OneTimeWorkRequestBuilder<Worker>.build()
    }

}
 

Редактирование Тестирования

 abstract class IWorkerContract(private val isLazy: Boolean = false) {
    private val lazyMgr = ResettableLazyManager()

    private val workRequest by if (isLazy) {
        // Type 'TypeVariable(<TYPE-PARAMETER-FOR-IF-RESOLVE>)' has no method 'getValue(Test, KProperty<*>)' and thus it cannot serve as a delegate
       resettableLazy(lazyMgr) {
          OneTimeWorkRequestBuilder<Worker>.build()
       }
    } else {
      OneTimeWorkRequestBuilder<Worker>.build()
    }
 

Ленивый делегат

 class ResettableLazy<PROPTYPE>(
    private val manager: ResettableLazyManager,
    private val init: () -> PROPTYPE,
) : Resettable {
    @Volatile
    private var lazyHolder = initBlock()

    operator fun getValue(thisRef: Any?, property: KProperty<*>): PROPTYPE = lazyHolder.value

    override fun reset() {
        lazyHolder = initBlock()
    }

    private fun initBlock(): Lazy<PROPTYPE> = lazy {
        manager.register(this)
        init()
    }
}

fun <PROPTYPE> resettableLazy(
    manager: ResettableLazyManager,
    init: () -> PROPTYPE,
): ResettableLazy<PROPTYPE> = ResettableLazy(manager, init)
 

Ответ №1:

значение всегда присваивается «Любому»

Да, потому что функция lazy { } создает новый экземпляр Lazy<OneTimeWorkRequest> , нет OneTimeWorkRequest , эти типы несовместимы. Я не совсем понимаю ваши требования, но проблему можно решить, предоставив пользовательскую Lazy реализацию, например

 class InitializedLazy<T>(override val value: T) : Lazy<T> {
    override fun isInitialized(): Boolean = true
}
 

Использование:

 abstract class IWorkerContract(private val isLazy: Boolean = false) {
    private val workRequest by if (isLazy) {
        lazy { OneTimeWorkRequestBuilder<Worker>().build() }
    } else {
        InitializedLazy(OneTimeWorkRequestBuilder<Worker>().build())
    }
}
 

Комментарии:

1. Это действительно работает, когда я использую kotlin по умолчанию «ленивый». Но когда я использую свой собственный «resettableLazy», я получаю ошибку. Я добавлю свой код

2. Может быть, вы можете добавить isLazy флаг в resettableLazy функцию и изменить initBlock() , например /* register */ return if (isLazy) { lazy { init() } } else { InitializedLazy(init()) }

3. @Andrew вы не следуете советам этого ответа. Обе ветви if/else должны возвращать делегата.

Ответ №2:

Вы могли бы разделить его на 2 отдельные переменные:

 abstract class IWorkerContract(private val isLazy: Boolean = false) {
    private val lazyWorkRequest by lazy {
        OneTimeWorkRequestBuilder<Worker>.build()
    }

    private val workRequest
        get() = when {
            isLazy -> lazyWorkRequest
            else -> OneTimeWorkRequestBuilder<Worker>.build()
        }
}
 

Из get() -за , lazyWorkRequest будет инициализирован не сразу, а только при необходимости.

Но что еще более важно: зачем нужно такое поведение, в чем вред от постоянного использования lazy?

Кроме того, какова предназначенная цель ResettableLazy ? Похоже, все, что вы хотите иметь var , и это решение для решения недостающего getValue() или Type mismatch . Это правильно?

Мне кажется, что ваш вопрос слишком специфичен, слишком техничен. Не могли бы вы объяснить, не используя Kotlin, какое поведение вам нужно?

Комментарии:

1. ResettableLazy-это делегат, значение которого вычисляется медленно, но может быть сброшено

Ответ №3:

Если вы получите доступ к своему свойству в конструкторе, оно будет вычислено во время создания экземпляра.

 class Foo(val isLazy: Boolean){
    val bar: Int by lazy { computeValue() }
    init { if (!isLazy) bar }
}