Tour of Scala

ミックスインを用いたクラス合成

Language

ミックスインはクラスを構成するのに使われるトレイトです。

abstract class A {
  val message: String
}
class B extends A {
  val message = "I'm an instance of class B"
}
trait C extends A {
  def loudMessage = message.toUpperCase()
}
class D extends B with C

val d = new D
println(d.message)  // I'm an instance of class B
println(d.loudMessage)  // I'M AN INSTANCE OF CLASS B

クラスDは スーパークラスをB とし、 ミックスインCを持ちます。 クラスは1つだけしかスーパークラスを持つことができませんが、ミックスインは複数持つことができます(キーワードはそれぞれextendswithを使います)。 ミックスインとスーパークラスは同じスーパータイプを持つことができます。

それでは抽象クラスから始まる興味深い例を見てみましょう。

abstract class AbsIterator {
  type T
  def hasNext: Boolean
  def next(): T
}

クラスは抽象型Tと標準的なイテレーターのメソッドを持ちます。 次に、(全ての抽象メンバーT, hasNext, nextが実装を持つ)具象クラスを実装します。

class StringIterator(s: String) extends AbsIterator {
  type T = Char
  private var i = 0
  def hasNext = i < s.length
  def next() = {
    val ch = s charAt i
    i += 1
    ch
  }
}

StringIteratorStringを受け取り、そのStringを反復処理するために使われます。 (例:Stringに特定の文字が含まれているかを確認するために)

それではAbsIteratorを継承したトレイトも作ってみましょう。

trait RichIterator extends AbsIterator {
  def foreach(f: T => Unit): Unit = while (hasNext) f(next())
}

このトレイトは(while (hasNext)で)要素がある限り、与えられた関数 f: T => Unitを次の要素(next())に対し連続して呼び出すforeachメソッドを実装しています。 RichIteratorはトレイトなので、RichIteratorはAbsIteratorの抽象メンバーを実装する必要がありません。

StringIteratorRichIteratorの機能を1つのクラスに組み合わせてみましょう。

class RichStringIter extends StringIterator("Scala") with RichIterator
val richStringIter = new RichStringIter
richStringIter foreach println

新しいクラスRichStringIterStringIteratorをスーパークラスとし、RichIteratorをミックスインとしています。

単一継承ではこのレベルの柔軟性を達成することはできないでしょう。

Contributors to this page: