【问题标题】:Kotlin var lazy initKotlin var 惰性初始化
【发布时间】:2017-12-22 21:59:57
【问题描述】:

我想对var 属性进行延迟初始化。由于by lazy 仅限于val 属性,因此我必须编写如下内容:

    private var currentContextProvider: ContextProvider? = null
        get() {
            if (field == null) {
                field = DefaultContextProvider()
            }
            return field
        }

现在我必须处理那些 无意义 可空性感知调用:currentContextProvider?.getContext()currentContextProvider!!.getContext()

我做错了吗?

【问题讨论】:

  • 听起来您对正在发生的两件事感兴趣:nullabilityinitialization。您的变量必须可以为空吗?是否要初始化此项目一次且仅一次?另外,你为什么要var 而不是val
  • 我相信有人会发布关于如何实现自定义委托的代码,但我无法想象任何现实生活中的情况,您需要在不影响封装的情况下修改惰性属性
  • @dkarmazi 我创建了一个默认策略是DefaultContextProvider。这就是我使用var 而不是val 的原因,所以我可以在运行时更改它。我还想提供一个存根策略(不是模拟整个班级)并测试所有东西。我的测试依赖项与 prod 依赖项区分开来,因此当它到达 DefaultContextProvider() 时会引发错误。这就是惰性初始化的原因。在对此类进行任何调用之前,我在onSetup 中设置了存根策略,一切正常,除非我必须处理可空性感知问题。

标签: delegates kotlin


【解决方案1】:

你可以决定用一些默认值来初始化它,而不是让它可以为空,在第一次访问时它将被延迟计算的值替换:

private val noInit = "noinit"
var currentContextProvider: String = noInit
        get() = if (field == noInit) {
            synchronized(this) {
                return if (field == noInit) "lazyinit" else field
            }
        } else field

(我已将ContextProvider 替换为String

自定义委托

以下实现custom delegate 重用前一个解决方案。通过定义var currentContextProvider: ContextProvider by LazyMutable { DefaultContextProvider() }可以像lazy()一样使用

class LazyMutable<T>(val initializer: () -> T) : ReadWriteProperty<Any?, T> {
    private object UNINITIALIZED_VALUE
    private var prop: Any? = UNINITIALIZED_VALUE

    @Suppress("UNCHECKED_CAST")
    override fun getValue(thisRef: Any?, property: KProperty<*>): T {
        return if (prop == UNINITIALIZED_VALUE) {
            synchronized(this) {
               return if (prop == UNINITIALIZED_VALUE) initializer().also { prop = it } else prop as T
            }
        } else prop as T
    }

    override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
        synchronized(this) {
            prop = value
        }
    }
}

【讨论】:

  • btw lazy() 至少实现了臭名昭著的双重检查锁定 - 如果你打开 Lazy.kt 源代码,你会发现它非常复杂
  • tbh 我认为这将是一个可重用的自定义委托 :)
  • 你去吧,虽然不像懒惰那样复杂;-)
  • 我们不应该将值保存在支持字段中吗:return if (prop == UNINITIALIZED_VALUE) initializer() else prop as T,但不只是放弃它?否则,它会在每次调用时运行initializer()(直到我们明确设置值)
  • 你是对的,抱歉我错过了那部分,用return if (prop == UNINITIALIZED_VALUE) initializer().also { prop = it } else prop as T 修复
【解决方案2】:

我需要一个惰性委托属性,它在您 get 属性时初始化和缓存,但允许您将其设置为 null 以删除该缓存结果(并在您 get 时重新initialize() 它再次)。

感谢上面对代码的回答,所以我可以调整它。

@Suppress("ClassName")
class lazyNullCacheable<T>(val initializer: () -> T) : ReadWriteProperty<Any?, T> {
    private object UNINITIALIZED_VALUE
    private var prop: Any? = UNINITIALIZED_VALUE

    @Suppress("UNCHECKED_CAST")
    override fun getValue(thisRef: Any?, property: KProperty<*>): T {
        return if (prop == UNINITIALIZED_VALUE || prop == null) {
            synchronized(this) {
                initializer().also { prop = it }
            }
        } else prop as T
    }

    override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
        synchronized(this) {
            prop = value
        }
    }
}

用法:

var prop: String? by lazyNullCacheable {
    "string"
}

prop // get prop
prop = null // when you're done using it and want to recalculate / cache it next time
prop // get prop, it will initialize() and cache again

功能上相当于做这样的事情(但这更丑)

var _prop: Type? = null
val prop: Type
    get() = _prop ?: run {
        _prop = Type()
        _prop!!
    }

prop // get result
_prop = null // clear cache
prop // next get will recalculate it

【讨论】:

    【解决方案3】:
    class LazyMutable<T>(
        val initializer: () -> T,
    ) : ReadWriteProperty<Any?, T> {
        private val lazyValue by lazy { initializer() }
        private var newValue: T? = null
    
        override fun getValue(thisRef: Any?, property: KProperty<*>) =
            newValue ?: lazyValue
    
        override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
            newValue = value
        }
    }
    

    用法:

    var foo by LazyMutable { "initial value" }
    

    (不支持可为空的foo

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2022-12-15
      • 2015-08-06
      • 2017-07-20
      • 2020-08-08
      • 1970-01-01
      • 2021-11-21
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多