演示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
,为什么不是5
或12
?
答案 0 :(得分:13)
回答原因:
编写时在Scala中
class A {
val a = 2
}
该值在类的构造函数中初始化(相同的行为适用于特征和对象)。此外,超类在子类之前初始化。这会导致您的用例出现以下行为:
创建 B
(内存保留),其中两个变量a
和b
的值为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