ジェネリッククラスはパラメータとして型を1つ受け取るクラスです。それらはコレクションクラスで特に役立ちます。
ジェネリッククラスの定義
ジェネリッククラスは角カッコ[]
の中にパラメータとして型を1つ受け取ります。 型パラメータの識別子として文字A
を使う習慣がありますが、任意のパラメータ名を使うことができます。
class Stack[A] { private var elements: List[A] = Nil def push(x: A): Unit = elements = x :: elements def peek: A = elements.head def pop(): A = { val currentTop = peek elements = elements.tail currentTop } }
このStack
クラスの実装はパラメータとして任意の型A
を受け取ります。 これはメンバーのリストvar elements: List[A] = Nil
が型A
の要素のみを格納できることを意味します。 手続きdef push
は型A
のオブジェクトのみを受け取ります (注: elements = x :: elements
は、x
を現在のelements
の先頭に追加した新しいリストをelements
に割り当て直します)。
ここで Nil
は空の List
であり、 null
と混同してはいけません。
使い方
ジェネリッククラスを使うには、角カッコの中にA
の代わりに型を入れます。
val stack = new Stack[Int] stack.push(1) stack.push(2) println(stack.pop) // prints 2 println(stack.pop) // prints 1
インスタンスstack
はIntのみを受け取ることができます。 しかしながら、型がサブタイプを持つ場合、それらは以下のように渡すことができます。
class Fruit class Apple extends Fruit class Banana extends Fruit val stack = new Stack[Fruit] val apple = new Apple val banana = new Banana stack.push(apple) stack.push(banana)
クラスApple
とBanana
は共にFruit
を継承しています。そのためFruit
のスタックにはapple
とbanana
のインスタンスを追加できます。
注意: ジェネリック型のサブタイプは*非変(invariant)*です。つまりStack[Char]
型の文字スタックがあるとき、それをStack[Int]
型の整数スタックとして使うことはできません。文字スタックに整数を入れることはできるので、このことは変に思えるかもしれません。結論としては、B = A
の場合に限り、Stack[A]
はStack[B]
の唯一のサブタイプとなります。これでは制限が強いので、ジェネリック型のサブタイプの振る舞いをコントロールするために、Scalaは型引数アノテーションの仕組みを提供します。