Tour of Scala

抽象型メンバー

Language

トレイトや抽象クラスのような抽象型は抽象型メンバーを持つことができます。 これは具体的な実装で実際の型を定義するという意味です。 こちらが例です。

trait Buffer {
  type T
  val element: T
}

こちらでは、抽象型type Tを定義しています。それはelementの型を記述するために使われます。このトレイトを抽象クラスで継承し、より具体的にするために上限型境界をTに追加することができます。

abstract class SeqBuffer extends Buffer {
  type U
  type T <: Seq[U]
  def length = element.length
}

Tの上限型境界の定義に出てきた、更に別の抽象型Uの使い方に気をつけてください。このclass SeqBufferはこのバッファーの中にシーケンスのみを保持することができます。それは型Tは新しい抽象型Uを使ったSeq[U]のサブタイプであると記述しているからです。

抽象型メンバーを持つトレイトとクラスは無名クラスのインスタンス化と組み合わせてよく使われます。 これを説明するために、今から整数のリストを参照するシーケンスバッファーを扱うプログラムを見てみます。

abstract class IntSeqBuffer extends SeqBuffer {
  type U = Int
}


def newIntSeqBuf(elem1: Int, elem2: Int): IntSeqBuffer =
  new IntSeqBuffer {
       type T = List[U]
       val element = List(elem1, elem2)
     }
val buf = newIntSeqBuf(7, 8)
println("length = " + buf.length)
println("content = " + buf.element)

ここで、ファクトリーnewIntSeqBufは抽象型Tを具体的な型List[Int]に設定するために、IntSeqBuf(つまりnew IntSeqBuffer)を無名クラスで実装します。

抽象型メンバーをクラスの型パラメータに変えることも、その逆も可能です。以下は上記コードの型パラメータのみを使うバージョンです。

abstract class Buffer[+T] {
  val element: T
}
abstract class SeqBuffer[U, +T <: Seq[U]] extends Buffer[T] {
  def length = element.length
}

def newIntSeqBuf(e1: Int, e2: Int): SeqBuffer[Int, Seq[Int]] =
  new SeqBuffer[Int, List[Int]] {
    val element = List(e1, e2)
  }

val buf = newIntSeqBuf(7, 8)
println("length = " + buf.length)
println("content = " + buf.element)

ここでは(+T <: Seq[U])をメソッドnewIntSeqBufから戻されるオブジェクトの具体的なシーケンス実装の型を隠すために 変位指定アノテーションを使わなければなりません。さらに、抽象型メンバをパラメータで置換することができないケースがあります。

Contributors to this page: