昨日と同じように、菱形継承問題を考える。
scala> trait TraitA {
     |     def greet(): Unit
     | }
defined trait TraitA
scala> trait TraitB extends TraitA {
     |     override def greet(): Unit = println("Good morning!")
     | }
defined trait TraitB
scala> trait TraitC extends TraitA {
     |     override def greet(): Unit = println("Good evening!")
     | }
defined trait TraitC
昨日と違うのは、TraitB と TraitC で greet メソッドを実装するときに override キーワードをつけているところだ。この場合、つぎのように、単純に2つを継承したクラスを作ってもエラーにならない。
scala> class ClassA extends TraitB with TraitC defined class ClassA
ここで、ClassA の greet メソッドを呼び出すと何を出力するか。試してみよう。
scala> (new ClassA).greet() Good evening!
Good evening! と表示された。ということは TraitC の greet メソッドが呼ばれたってことだ。
つぎに、TraitB と TraitC の継承順を入れ替えた ClassB を考えよう。
scala> class ClassB extends TraitC with TraitB defined class ClassB scala> (new ClassB).greet() Good morning!
今度は TraitB の greet メソッドが呼び出されている。
Scala では、こういう形で複数のトレイトを継承(ミックスイン)した場合、あとからミックスインしたトレイトが優先される。この機能をトレイトの線形化と呼ぶ。
さて、ここからがちょっとよくわからない。メソッドの中で super を使うことで親トレイトのメソッドを呼び出すことができる。定義はこんなふうだ。
scala> trait TraitA {
     |     def greet(): Unit = println("Hello!")
     | }
defined trait TraitA
scala> trait TraitB extends TraitA {
     |     override def greet(): Unit = {
     |         super.greet()
     |         println("My name is Terebi-chan.")
     |     }
     | }
defined trait TraitB
scala> trait TraitC extends TraitA {
     |     override def greet(): Unit = {
     |         super.greet()
     |         println("I like niconico.")
     |     }
     | }
defined trait TraitC
scala> class ClassA extends TraitB with TraitC
defined class ClassA
scala> class ClassB extends TraitC with TraitB
defined class ClassB
ClassA の greet メソッドを呼んでみよう。
scala> (new ClassA).greet() Hello! My name is Terebi-chan. I like niconico.
あとからミックスインされた TraitC の greet メソッドが呼ばれるはずだから I like niconico. と出力されるのはわかる。super を使って親トレイトの greet メソッドも呼んでいるんだから Hello! と出力されるのもわかる。だけどなんで My name is Terebi-chan. と出力されるんだ?これは TraitB のメソッドの出力のはずだろう?
TraitB と TraitC の間には親子関係がないはずなのに、まるで TraitB が TraitC の親トレイトであるかのようになっている。線形化ってそういうものなのか?
ClassC では出力の順番が変わる。
scala> (new ClassB).greet() Hello! I like niconico. My name is Terebi-chan.
線形化の順番が違うので出力の順番も違うってことだろう。とは思うけど、なんとなく納得がいかない。