错误或功能:Kotlin允许在继承中将“val”更改为“var”
我刚开始探索Kotlin语言。 我正在努力继承,var和val和副作用。
如果我声明一个具有val x
的特征A
并且在AImpl
覆盖x
,则可以将其重写为var
(参见下面的代码)。 令人惊讶的是A
的print()
方法受x
的重新分配影响,即使x
是A
的值。 这是一个错误还是一个功能?
码:
trait A { fun print() { println("Ax = $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() // Ax = 2 ax = 3; // x can be changed // even though print() is defined in trait A // where x is val it prints x = 3 a.print() // Ax = 3 }
我意识到这样一个事实,即如果我明确定义a
类型A
,不允许更改x
:
val a = AImpl(2) : A ax = 3 // ERROR: value x cannot be reassigned
但是,正如第一个案例所显示的那样,继承可能导致副作用,这在A
中显然不是有意A
。 如何保护值不被继承改变?
你可以让你的val
final
,即禁止压倒一切。 如果你在一个类中定义了一个val
,那么默认是final
的。
另外,如果你需要用一个var
覆盖val
,但是不想让setter公开,你可以这样说:
override var x = 1 private set
用var
覆盖val
是一个特性 。 这相当于添加一个set方法,而在超类中只有一个get方法。 这在实现一些模式(如只读接口)时非常重要。
没有办法“保护”你的val
不被重写,而是允许改变变化,而不是final
,因为val
并不意味着“不变的参考”,而只是“只读属性”。 换句话说,当你的特质A
声明一个val
,这意味着通过类型A
的引用客户不能写这个val
,没有其他的保证是有意的,或者确实是可能的。
PS分号在Kotlin中是可选的,完全可以忽略它们
我会认为这是一个功能,因为更改VAL到VAR施加较弱的使用限制, 并且不能破坏任何超类代码 。 可见性修改器可以观察到类似的情况:
trait A { protected fun print() { ... } } class AImpl: A { public override fun print() { ... } }
在这个例子中,可见性限制也被一个子类放宽,尽管有些人正在把这个技术看作一个反模式。
如何保护值不被继承改变?
在kotlin中,你可以明确地定义是否任何特定的类成员可以被使用open
修饰符的子类覆盖。 然而,在特征上,所有成员都是默认开放的。 解决的办法是用类替换特质,所以你可以控制继承:
abstract class A { fun print() { ... } val x : Int = 2; } class AImpl(x : Int) : A() { override var x = x // compilation error }