daybreaksnow's diary

私を私と呼びたい

[Scala]入門 trait

以下の連載の7回目読んだ。
http://www.atmarkit.co.jp/ait/kw/scala.html


インターフェース的な存在がトレイト(trait)。ただし実装も書ける。コンストラクタも書けるが引数は渡せない。
複数のトレイトを継承することをミックスインという。コンストラクタはミックスインした左側から順に呼ばれる。

一つめのトレイトはextendsで、二つ目以降はwithを使う。なぜwithだけでないのか謎。

trait Base{
  def print() = println("base")
  def write()
}

trait Piyo {
  def piyo() = println("Piyo")
}

class Hoge extends Base with Piyo{
  def write = println("hoge")
}

インスタンス化する時にもミックスインできる

val fuga = new Fuga with Piyo
fuga.piyo

指定したトレイトのメソッドを呼ぶ

trait Base{
  def print() = println("base")
}

trait Piyo {
  def piyo() = println("Piyo")
  def print() = piyo()
}

class Hoge extends Base with Piyo{
  override def print() = super[Piyo].print
}

複数のトレイトを実装した無名クラスも作れる

val foo = new Base with Piyo{
  def write = println
}

abstract override★

テンプレートメソッドパターン的なことができる。

trait Base{
  def work(time : Int)
}

trait Half extends Base{
  abstract override def work(time:Int) = super.work(time/2)
}

trait Sub extends Base{
 abstract override def work(time:Int) = super.work(time-10)
}

class Worker extends Base{
 def work(time:Int) = println(time)
}

--
val worker = new Worker() with Half
worker.work(100) //Half.work→Worker.workが呼ばれる。出力値は50

val worker2 = new Worker() with Half with Sub
worker.work(100) //Sub.work→Half.work→Worker.workが呼ばれる。出力値は(100-10)/2で45

メソッド呼び出しは基本的にはwithの右側から解決。基本でない場合はトレイトの線形化でぐぐる。