lazy val

Scalaで結構嵌ったこと。

trait A { val a: Int }
trait B extends A { val b: Int; val a = b }
object C extends B { val c = 3; val b = c }

こういう風に定義すると、

scala> C.a
res17: Int = 0

となる。しかし、C.a=3となってほしい。
コップ本第20章「抽象メンバー」に答えが書いてあった。ポイントは、スーパークラスから順番に初期化されるということ。
trait Bでval a = bとした時点では、bはデフォルト値0をとるからa=0になるようだ。
正解は、次のようにする。

trait A { val a: Int }
trait B extends A { val b: Int; lazy val a = b }
object C extends B { val c = 3; val b = c }

スーパークラスから継承したフィールドを初期化する際にlazyをつける。これで、

scala> C.a
res20: Int = 3

となり、期待する動作になる。lazyをつけると、定義時ではなく初めて参照される時点で初期化される。
結論は、traitまたはabstract classで他の値を参照してvalを初期化する際はlazyをつけること。

第20章では他にも、抽象型、パス依存型、構造的サブ型という重要な概念が紹介されている。
コップ本、ちゃんと読まなきゃね・・・