Scala ひと巡り : 抽象型 (Abstract Types)

Scala では、クラスは値(コンストラクタ・パラメータ)と(もしクラスがジェネリック [16]なら)型でパラメータ化されます。単に規則に従って、オブジェクトメンバーとして値を持てるというばかりではありません; 値と同様に、型はオブジェクトのメンバーです。さらに、メンバーの両形式とも、具象あるいは抽象で構いません。

次の例は、クラス Buffer のメンバーとして、延期された値定義と抽象型定義の両方を定義しています。

abstract class Buffer { type T val element: T }

抽象型はその正体が正確には知られていない型です。上記の例で、我々は、クラス Buffer の各オブジェクトが 型メンバー T を持つことだけを知っています。しかしクラス Buffer の定義は、メンバー型 T がどのような具象(具体的な)型に対応するのかを明らかにしません。値定義と同じように、サブクラス中で型定義をオーバライドできます。これにより、(可 能な、抽象型の具象インスタンス化を記述する)型境界を厳しくすることで、抽象型についてより多くの情報を明らかにできます。

次のプログラムで、型 T が新しい抽象型 U のサブ型でなければならないと述べることで、バッファ中にシーケンスのみを記憶できるクラス SeqBuffer を得ます:

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

抽象型メンバーをもつトレイトあるいはクラス [2]は、無名クラスのインスタンス化との組合せでしばしば使われます。この例として、整数リストを参照するシーケンスバッファを扱う、次のプログラムを見てみます。:

abstract class IntSeqBuffer extends SeqBuffer { type U = Int } object AbstractTypeTest1 extends Application { 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 の戻り値型は、型 U が toInt に等しい、トレイト Buffer の特化を参照します。メソッド newIntSeqBuf 本体内における無名クラスのインスタンス化で、似たような型エイリアスを使っています。そこでは、型 T が List[Int]を参照する、IntSeqBuffer の新しいインスタンスを生成します。

抽象型メンバーをクラスの型パラメータに変えることや、その逆も可能であることに注意してください。次は、上記コードの型パラメータだけを使うバージョンです:

abstract class Buffer[+T] { val element: T } abstract class SeqBuffer[U, +T <: Seq[U]] extends Buffer[T] { def length = element.length } object AbstractTypeTest2 extends Application { 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) }

ここでは変位指定アノテーション [17]を使う必要があることに注意してください; そうでなければ、メソッド newIntSeqBuf が返すオブジェクトの具象シーケンス実装型を隠せなくなります。さらにまた、型パラメータを抽象型で置き換えできない場合があります。