错误或特征:Kotlin允许在继承中将'val'更改为'var'

时间:2014-04-02 18:54:42

标签: inheritance side-effects kotlin

我刚开始探索Kotlin语言。我在继承,变异和缬氨酸以及副作用方面苦苦挣扎。

如果我使用A声明特征val x并在x覆盖AImpl,则可以将其覆盖为var(请参阅下面的代码) 。令人惊讶的是,print()中的A方法受x重新分配的影响,即使xA中的值。这是一个错误还是一个功能?

代码:

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明显无意的副作用。如何保护值不被继承更改?

2 个答案:

答案 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
}