トレイトや抽象クラスのような抽象型は抽象型メンバーを持つことができます。 これは具体的な実装で実際の型を定義するという意味です。 こちらが例です。
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
から戻されるオブジェクトの具体的なシーケンス実装の型を隠すために 変位指定アノテーションを使わなければなりません。さらに、抽象型メンバをパラメータで置換することができないケースがあります。