Kotlin:使用自定义setter时没有延迟的解决方法?

时间:2017-09-22 14:15:18

标签: android kotlin

在我的活动中,我有一个不可为空的字段,并且有一个自定义setter。我想在onCreate方法中初始化字段,因此我将lateinit添加到我的变量声明中。但是,显然你不能这样做(目前):https://discuss.kotlinlang.org/t/lateinit-modifier-is-not-allowed-on-custom-setter/1999

这些是我能看到的解决方法:

  • 以Java方式做。使字段可为空并使用null初始化它。我不想这样做。
  • 使用该类型的“默认实例”初始化字段。这就是我现在所做的。但对某些类型来说这太贵了。

有人可以推荐更好的方式(不涉及删除自定义设置器)吗?

3 个答案:

答案 0 :(得分:9)

将其替换为由nullable属性支持的属性:

    var _tmp: String? = null
    var tmp: String
        get() = _tmp!!
        set(value) {_tmp=value; println("tmp set to $value")}

或者这样,如果你希望它与lateinit语义一致:

var _tmp: String? = null
var tmp: String
    get() = _tmp ?: throw UninitializedPropertyAccessException("\"tmp\" was queried before being initialized")
    set(value) {_tmp=value; println("tmp set to $value")}

答案 1 :(得分:2)

这可以通过使用backing property来实现(根据Pavlus的回答);但是,我更喜欢将它包装在delegate内以避免将其暴露在属性的上下文之外:

open class LateInit<T: Any> : ReadWriteProperty<Any?, T> {
    protected lateinit var field: T

    final override fun getValue(thisRef: Any?, property: KProperty<*>) = get()
    final override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) = set(value)

    open fun get() = field
    open fun set(value: T) { field = value }
}

这提供了可以使用自定义实现覆盖的标准getter和setter:

var upperCaseString by object : LateInit<String>() {
    override fun set(value: String) {
        field = value.toUpperCase()
    }
}

但是,由于此实现需要扩展委托,因此无法从属性类型推断泛型类型。这可以通过将自定义getter和setter作为参数来克服:

class LateInit<T: Any>(private val getter: FieldHolder<T>.() -> T = { field },
                       private val setter: FieldHolder<T>.(T) -> Unit = { field = it }) :
        ReadWriteProperty<Any?, T> {
    private val fieldHolder = FieldHolder<T>()

    override fun getValue(thisRef: Any?, property: KProperty<*>) = fieldHolder.getter()
    override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) =
            fieldHolder.setter(value)

    class FieldHolder<T: Any> {
        lateinit var field: T
    }
}

然后可以这样使用:

private var upperCaseString: String by LateInit(setter = { field = it.toUpperCase() })

答案 2 :(得分:0)

我意识到你也可以让你的私有财产 lateinit 而不是让它可以为空:

var tmp: T
    get() = _tmp
    set(value) {
        _tmp = value
        println("tmp set to $value")
    }

private lateinit var _tmp: T