演示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)
Run Code Online (Sandbox Code Playgroud)
它打印价值:2
,为什么不是5
或12
?
kir*_*uku 13
要回答原因:
在Scala中你写的时候
class A {
val a = 2
}
Run Code Online (Sandbox Code Playgroud)
该值在类的构造函数中初始化(相同的行为适用于特征和对象).此外,超类在子类之前初始化.这会导致您的用例出现以下行为:
B
是创建的(内存是保留的),有两个变量a
,b
其值为0.现在A
调用构造函数.因为a
在子类中被覆盖并且由于Scalas动态绑定性质,它不会被赋值为2,而是具有子类的值.你想成为10,但因为这个赋值发生在B
(尚未调用)的构造函数中,所以分配了默认值0.现在,b
被分配.因为它没有被覆盖,所以a+2
选择了值,其中a
是0.因为构造函数A
在这里完成,所以B
可以调用构造函数,它将10分配给a
.
因此,a
是10并且b
是2.
要回答如何对付此行为错误:
只要您不完全了解可能出现的问题,请不要使用val.使用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
Run Code Online (Sandbox Code Playgroud)