Search code examples
androidkotlinandroid-workmanager

How to assign a property either lazily or normally in a conditional way


I would like to assign one property either lazy or in a "normal way", but the problem is, that my value is always cast to "Any". I cannot use the "by" keyword, when I assign a property conditionally. Here is my current approach

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()
    }

}

Edit Testing

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()
    }

Lazy Delegate

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)

Solution

  • You could split it up in 2 separate variables:

    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()
            }
    }
    

    Because of get(), lazyWorkRequest will not be initialised immediately but only when needed.

    But more importantly: why is this behaviour needed, what is the harm of always using lazy?

    Also, what is the intended purpose of ResettableLazy? It looks like all you want to have a var and this is the solution to solve the missing getValue() or Type mismatch. Is that correct?

    It feels to me your question is too specific, too technical. Could you explain without using Kotlin what kind of behaviour you need?