18

我刚开始探索 Kotlin 语言。我正在为继承、var&val 和副作用而苦苦挣扎。

如果我A用 aval x和覆盖声明一个特征xAImpl则可以将其覆盖为var(参见下面的代码)。令人惊讶的是,即使是 in 的值,in的print()方法A也会受到重新分配的影响。这是错误还是功能?xxA

代码:

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. 如何保护值不被继承改变?

4

2 回答 2

31

你可以让你的val final,即完全禁止覆盖它。如果您val在类中定义 a,则final默认为它。

此外,如果您需要val用 a覆盖 a var,但不希望 setter 公开,您可以这样说:

override var x = 1
    private set

val用 a覆盖 avar 是一项功能。这相当于添加了一个 set-method,而在超类中只有一个 get-method。这在实现一些模式时相当重要,例如只读接口。

没有办法“保护”你val不被覆盖final,因为val它不意味着“不可变引用”,而仅仅是“只读属性”。换句话说,当您的 traitA声明 a时val,这意味着通过类型的引用,A客户端无法编写 this val,没有其他保证,或者确实可能。

PS 分号在 Kotlin 中是可选的,可以完全省略它们

于 2014-04-03T07:22:31.343 回答
3

我认为这是一个功能,因为将 val 更改为 var 会施加较弱的使用限制,并且不会破坏任何超类代码。使用可见性修饰符可以观察到类似的情况:

trait A {
  protected fun print() {
    ...
  }
}

class AImpl: A {
  public override fun print() {
    ...
  }
}

在这个例子中,可见性限制也被一个子类放宽了,尽管有些人认为这种技术是一种反模式。

如何保护值不被继承改变?

在 kotlin 中,您可以显式定义任何特定的类成员是否可以被子类使用open修饰符覆盖。然而,在特征中,默认情况下所有成员都是打开的。解决方案是用类替换 trait,这样你就可以控制继承:

abstract class A {
  fun print() {
    ...
  }

  val x : Int = 2;
}

class AImpl(x : Int) : A() {
  override var x = x // compilation error
}
于 2014-04-03T07:23:29.597 回答