トレイトを使いこなしたい

## はじめに

Scalaのトレイトについて調べてみます。この記事の内容は、ミックスインに関心がある方であれば、興味を持っていただけると思います。

トレイトはScalaのコード再利用の基本単位です。コードの再利用も大切なのですが、ここでは、主に線形化と呼ばれる機能について調べてみたいと思います。

## オーバーライドしたら、どうなるの？

まずは、簡単な例を確認してみます。この例は複数のトレイトをミックスインして、トレイトでnameメソッドをオーバーライドしています。

``` scala
object Main extends App {
  trait T {
    def name: String
  }
  trait Ta extends T {
    override def name: String = "ta"
  }
  trait Tb extends T {
    override def name: String = "tb"
  }
  trait Tc extends T {
    override def name: String = "tc"
  }

  class A extends T with Ta with Tb with Tc {
    override def name = super.name
  }

  val a = new A
  println(a.name) // #=> tc
}
```

ミックスインしたnameが複数ありますが、Tcのnameが採用されましたね。

ちょっと変更してみましょう。

``` scala
object Main extends App {
  trait T {
    def name: String
  }
  trait Ta extends T {
    override def name: String = "ta"
  }
  trait Tb extends T {
    override def name: String = "tb"
  }
  trait Tc extends T {
    override def name: String = "tc"
  }

  class A extends T with Tb with Tc with Ta {
    override def name = super.name
  }

  val a = new A
  println(a.name) // #=> ta
}
```

この場合は、Taのnameが採用されましたね。

また同じように、ちょっと変更してみましょう。

``` scala
object Main extends App {
  trait T {
    def name: String
  }
  trait Ta extends T {
    override def name: String = "ta"
  }
  trait Tb extends T {
    override def name: String = "tb"
  }
  trait Tc extends T {
    override def name: String = "tc"
  }

  class A extends T with Tc with Ta with Tb {
    override def name = super.name
  }

  val a = new A
  println(a.name) // #=> tb
}
```

この場合は、Tbのnameが採用されましたね。

本に書いてあるとおりですね。複数のトレイトで同じメソッドをオーバーライドした場合は、右側のトレイトのオーバーライドが採用されます。

## 右側のトレイトでsuper.nameしたら、どうなるの？

super経由でメソッドを呼び出すと、親のメソッドを呼び出すことができます。ここでは、右側のトレイトでsuper.nameメソッドを呼び出したら、どうなるのかを確認してみます。

まずは、試してみましょう。

``` scala
object Main extends App {
  trait T {
    def name: String = "t"
  }
  trait Ta extends T {
    override def name: String = super.name + " -> ta"
  }
  trait Tb extends T {
    override def name: String = super.name + " -> tb"
  }
  trait Tc extends T {
    override def name: String = super.name + " -> tc"
  }

  class A extends T with Tc with Ta with Tb {
    override def name = super.name
  }

  val a = new A
  println(a.name) // #=> t -> tc -> ta -> tb
}
```

トレイトの並び順と同じ順番（`T -> Tc -> Ta -> Tb`）で親のメソッドが呼び出されたように見えますが、騙されてはいけません。さっきは右側のトレイトが採用されたのですから、`Tb -> Ta -> Tc -> T`という順番で呼び出されるはずです。（本にも書いてあります。。）

ちょっと変更してみましょう。

``` scala
object Main extends App {
  trait T {
    def name: String = "t"
  }
  trait Ta extends T {
    override def name: String = "ta -> " + super.name
  }
  trait Tb extends T {
    override def name: String = "tb -> " + super.name
  }
  trait Tc extends T {
    override def name: String = "tc -> " + super.name
  }

  class A extends T with Tc with Ta with Tb {
    override def name = super.name
  }

  val a = new A
  println(a.name) // #=> tb -> ta -> tc -> t
}
```

今度は、期待した順番（`Tb -> Ta -> Tc -> T`）で親のメソッドが呼び出されたように見えます。良さそうですね。

ちょっと分かりにくいかもしれませんが、どちらも`Tb -> Ta -> Tc -> T`という順番で呼び出されています。

## 線形化とは？

クラス構造を直列にする仕組みだと思います。

たとえば、さっきの例のクラス構造は、次のようになると思います。（クラス構造とは言わないかもしれませんが・・）

```
T
↑
Tc
↑
Ta
↑
Tb
↑
A
```

Aの親はTb、Tbの親はTa、Taの親はTc、Tcの親はTという図です。

## なにが嬉しいの？

あまり理解できていないのですが、superを呼び出すメソッドは呼び出し先の振る舞いを変えることができるとのことです。

さっきの例で、`Tb -> Ta -> Tc -> T`という順番で親のメソッドが呼び出されたのが、`T -> Tc -> Ta -> Tb`という順番で呼び出されたかように見えました。

このようなことができる理由は、superを呼び出すメソッド側で<b>superを呼び出す前</b>と__superを呼び出した後__に処理を追加できるからだと思います。

当然、副作用のある処理を追加することだって、できます。（やっていいとは、言っていませんが、できます。）

## まとめ

もっと複雑なケースを検証しなければ、なんとも言えませんが、かなりクラス構造がシンプルだということに気付きました。

このトレイトを使いこなせたら、いろいろな機能をキレイに実装できるようになるのではと期待しています。

## 参照URL

- [第9章：Scalaのトレイト(trait)](http://qiita.com/f81@github/items/5b96af593812286eec49)
- [Trait in scala](http://www.slideshare.net/YutaShimakawa/trait-in-scala-34562912)

## 参考文献

- Scala スケーラブルプログラミング [第２版]