如果我使用主构造函数来初始化一个属性,它的 setter 不会被调用:
open class Foo(open var bar: Any)
open class Baz(bar: Any) : Foo(bar) {
override var bar: Any
get() = super.bar
set(_) = throw UnsupportedOperationException()
}
...
Baz(1) // works, no setter is called
这也有效:
open class Foo(bar: Any) {
open var bar: Any = bar // no exception in Baz(1)
}
但这不会:
open class Foo {
open var bar: Any
constructor(bar: Any) {
this.bar = bar // UnsupportedOperationException in Baz(1)
}
}
这甚至无法编译:
open class Foo(bar: Any) {
open var bar: Any // Property must be initialized or be abstract
init {
this.bar = bar
}
}
我之所以这么问是因为我需要让 bar
lateinit
才能做到这一点:
Baz(1) // works
Foo(2) // this too
Foo().bar = 3 // should work too
但这在 Kotlin 中不起作用:
// 'lateinit' modifier is not allowed on primary constructor parameters
open class Foo(open lateinit var bar: Any)
而且没有这样的语法:
open class Foo() {
open lateinit var bar: Any
constructor(bar: Any) {
// instead of this.bar
fields.bar = bar // or whatever to bypass the setter
}
}
任何想法如何解决这个问题? (除了反射,添加额外的属性和委托)
答案 0 :(得分:0)
要允许 Foo()
对象存在,您需要为 bar
属性提供一些初始值(或使其为 lateinit
)。但是 open lateinit
属性不能在构造函数/init 块中初始化,因为它导致 leaking 'this' in constructor。
所以有两种选择:
lateinit
修饰符并为 Foo
提供工厂函数而不是二级构造函数(还需要修改 Baz
类):open class Foo {
open lateinit var bar: Any
}
fun Foo(bar: Any): Foo = Foo().also { it.bar = bar }
open class Baz(bar: Any) : Foo() {
init {
super.bar = bar
}
override var bar: Any
get() = super.bar
set(_) = throw UnsupportedOperationException()
}
bar
属性使用一些虚拟默认值并添加自定义 getter 来模拟 lateinit
属性行为:open class Foo(bar: Any = NONE) {
companion object {
private object NONE
}
open var bar = bar
get() = field.takeIf { it != NONE } ?: throw UninitializedPropertyAccessException()
}
但是请注意,在基类中设置属性成为可能,而在派生类中禁止它是一种糟糕的类设计,因为它破坏了 Liskov substitution principle。