ちょっとしたことから、Scalaのscala.collection.MapLikeのソースを見ていたのですが、こんな記述があるのを見つけました。
// Source from Scala 2.9.2 ... package scala.collection import generic._ import mutable.{ Builder, MapBuilder } import annotation.{migration, bridge} import parallel.ParMap 〜省略〜 trait MapLike[A, +B, +This <: MapLike[A, B, This] with Map[A, B]] extends PartialFunction[A, B] with IterableLike[(A, B), This] with GenMapLike[A, B, This] with Subtractable[A, This] with Parallelizable[(A, B), ParMap[A, B]] { 〜省略〜 override /*PartialFunction*/ def toString = super[IterableLike].toString }
問題は、これ。
override /*PartialFunction*/ def toString = super[IterableLike].toString
なんか、superにトレイト名をパラメータとして与えてますね。これって、Mix-inしたトレイトのうち、どれを呼び出すか指定できるってこと?
ちょっと気になったので、試してみることにしました。
まずは、こんなソースを用意。
// TraitTest.scala trait TraitA { def action(): Unit = println("This is TraitA!!") } trait TraitB { def action(): Unit = println("This is TraitB!!") } class Sample extends TraitA with TraitB { override def action(): Unit = super.action() } val s = new Sample s.action()
同じactionというメソッドを実装したトレイトを2つMix-inしたクラスですね。
実行すると、こうなります。
$ scala TraitTest.scala This is TraitB!!
同じメソッドがあった場合、1番右(最後)にMix-inされたトレイトが呼び出されることになるので、Mix-inの順番をこう変えると
class Sample extends TraitB with TraitA {
結果はこうなります。
$ scala TraitTest.scala This is TraitA!!
ここまでは、まあ普通ですね。
では、TraitAを最後にMix-inしたままactionメソッドの実装をちょっと変えてみます。
class Sample extends TraitB with TraitA { override def action(): Unit = super[TraitB].action() }
結果は、こう。
$ scala TraitTest.scala This is TraitB!!
TraitAが最後にMix-inされているにも関わらず、TraitBの実装を呼び出すことができてしまいました…。
今度は、これにクラスの継承を加えてみましょう。とりあえず、superキーワードは指定なしに戻してみます。
class Parent { def action(): Unit = println("This is Parent!!") } class Sample extends Parent with TraitB with TraitA { override def action(): Unit = super.action() }
実行。
$ scala TraitTest.scala This is TraitA!!
まあ、そうですよね…。
では、こう変更。
class Sample extends Parent with TraitB with TraitA { override def action(): Unit = super[Parent].action() }
実行。
$ scala TraitTest.scala This is Parent!!
…トレイトを飛び越えて、継承したクラスのメソッドを呼べてしまいました。
これ、全然知らなかったです。MapLikeのソースを見ていたのは全然違う調べ物をしていたからなのですが、意外なことがわかってしまいました。
ちなみに、指定できるのは直接継承しているクラスまたはMix-inしているトレイトだけのようで、そのクラスやトレイトのさらに上位クラスなどは指定できないようです。
trait SuperTrait { def action(): Unit = println("This is SuperTrait!!") } trait TraitA extends SuperTrait { override def action(): Unit = println("This is TraitA!!") } class Sample extends Parent with TraitB with TraitA { override def action(): Unit = super[SuperTrait].action() }
みたいなことをすると…
$ scala TraitTest.scala /xxxxx/TraitTest.scala:26: error: SuperTrait does not name a parent class of class Sample super[SuperTrait].action() ^ one error found
そんな名前のクラス、親クラスにいないんだけど、と怒られてしまいます。この例は、トレイトの上位トレイトをつけたものですが、クラスでやっても同じでした。
まあ、実際にはそれほど使う機会はないような気もしますが、知識の片隅に入れておきましょう。
なお、後で調べてみると、すでにまとめておられる方がいました。
Scalaトレイト メモ(Hishidama's Scala trait Memo)
こちらのサイトは、Javaの調べ物でかなりお世話になっています。さすがですね♪