笔记来源:
Scala谜题
在许多面向对象的语言中,常常在类构造器中接受参数,目的是将参数赋值给类成员。
执行下面的代码会是什么结果呢?
trait A {
val audience: String
println("Hello " + audience)
}
class BMember(a: String = "World") extends A {
val audience = a
println("I repeat: Hello " + audience)
}
class BConstructor(val audience: String = "World") extends A {
println("I repeat: Hello " + audience)
}
new BMember("Readers")
new BConstructor("Readers")
答案是:
Hello null
I repeat:Hello Readers
Hello Readers
I repeat:Hello Readers
通常,BConstructor
中的模式是首选的,因为它的行为更少可能会引起意外。这时,超类中声明的 val
绝不会存在于非初始化的状态。
其实,超类和超特质初始化代码的执行是在参数评估和早期字段定义之后,实例化类和特质的初始化语句之前。直接超类和混进特质是当它们出现在 class
、trait
、object
定义中时按从左到右的顺序初始化。
因此,考虑以下初始化方式:
class BMember2(a: String = "World") extends {
val audience = a
} with A {
println("I repeat: Hello " + audience)
}
new BMember2("Readers")
它的输出结果则为:
Hello Readers
I repeat:Hello Readers
实际生产中,最好考虑在类或对象体的括号后(这是主构造器)按照从左到右的声明顺序插入超类构造器和超特质初始化程序。