我刚开始探索Kotlin语言。我在继承,变异和缬氨酸以及副作用方面苦苦挣扎。
如果我使用A
声明特征val x
并在x
覆盖AImpl
,则可以将其覆盖为var
(请参阅下面的代码) 。令人惊讶的是,print()
中的A
方法受x
重新分配的影响,即使x
是A
中的值。这是一个错误还是一个功能?
代码:
trait A {
fun print() {
println("A.x = $x")
}
val x : Int;
}
class AImpl(x : Int) : A {
override var x = x; // seems like x can be overriden as `var`
}
fun main(args: Array<String>) {
val a = AImpl(2)
a.print() // A.x = 2
a.x = 3; // x can be changed
// even though print() is defined in trait A
// where x is val it prints x = 3
a.print() // A.x = 3
}
我知道如果我明确定义a
类型A
,则不允许更改x
:
val a = AImpl(2) : A
a.x = 3 // ERROR: value x cannot be reassigned
但是,正如第一个案例所示,继承可能会导致A
明显无意的副作用。如何保护值不被继承更改?
答案 0 :(得分:27)
您可以制作val
final
,即禁止覆盖它。
如果您在类中定义val
,则默认情况下为final
。
另外,如果您需要使用val
覆盖var
,但又不想公开设置者,则可以这样说:
override var x = 1
private set
使用val
覆盖var
是一项功能。它相当于添加set方法,而在超类中只有一个get方法。这对于实现某些模式非常重要,例如只读接口。
没有办法保护&#34;您的val
被覆盖的方式允许更改变异而不是变为final
,因为val
并不意味着&#34;不可变参考&#34;,而仅仅是&#34; ;只读属性&#34;。换句话说,当您的特征A
声明val
时,表示通过A
类型的引用,客户端无法写入此val
},没有其他保证,或确实可能。
P.S。分号在Kotlin中是可选的,完全可以省略它们
答案 1 :(得分:3)
我认为这是一个功能,因为将val更改为var会限制较弱的使用限制,不能破坏任何超类代码。使用可见性修饰符可以观察到类似的情况:
trait A {
protected fun print() {
...
}
}
class AImpl: A {
public override fun print() {
...
}
}
在此示例中,子类也放宽了可见性限制,尽管有些人将此技术视为反模式。
如何保护值不被继承更改?
在kotlin中,您可以使用open
修饰符明确定义子类是否可以覆盖任何特定的类成员。但是,在特征中,默认情况下所有成员都是打开的。解决方案是用class替换trait,这样你就可以控制继承了:
abstract class A {
fun print() {
...
}
val x : Int = 2;
}
class AImpl(x : Int) : A() {
override var x = x // compilation error
}