当覆盖一个特征时,为什么价值很奇怪?

时间:2014-08-05 14:39:58

标签: scala traits

演示scala代码:

trait A {
  val a = 3
  val b = a + 2
}

trait B extends A {
  override val a = 10
}

object X extends B

println(X.b)

它打印价值:2,为什么不是512

1 个答案:

答案 0 :(得分:13)

回答原因:

编写时在Scala中

class A {
  val a = 2
}

该值在类的构造函数中初始化(相同的行为适用于特征和对象)。此外,超类在子类之前初始化。这会导致您的用例出现以下行为:

创建

B(内存保留),其中两个变量ab的值为0.现在调用A的构造函数。因为a在子类中被覆盖并且由于Scalas动态绑定性质,所以它不会被赋值为2,而是具有子类的值。你想成为10,但因为这个赋值发生在B的构造函数中(尚未调用),所以分配了默认值0。现在,b已分配。因为它没有被覆盖,所以选择值a+2,其中a为0.因为A的构造函数在这里完成,所以可以调用B的构造函数,将10分配给a

因此,a为10,b为2。

要回答针对此行为错误的操作:

只要你不完全了解可能出现的问题,就不要使用vals。使用defs或lazy val代替,值不会在类的构造函数中初始化,因此可以轻松覆盖。如果你绝对需要一个特征中的val,那么最后

可以将var标记为独立于子类的初始化,可以使用var a: Type = _来完成。这告诉编译器不要在定义类的构造函数中初始化此变量(但意味着该值需要保持可变)。然后可以在子类中轻松分配它。当调用作为方法的超类的构造函数时,这很重要,它初始化var:

class A {
  f()
  def f() = ()
}

class B extends A {
  // don't initialize this var with anything else here or
  // the later assignment will be overwritten
  var b: Int = _
  override def f() =
    b = 5
}

new B().b // prints 5