ジェネリッククラスはパラメータとして型を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)

クラスAppleBananaは共にFruitを継承しています。そのためFruitのスタックにはapplebananaのインスタンスを追加できます。

注意: ジェネリック型のサブタイプは*非変(invariant)*です。つまりStack[Char]型の文字スタックがあるとき、それをStack[Int]型の整数スタックとして使うことはできません。文字スタックに整数を入れることはできるので、このことは変に思えるかもしれません。結論としては、B = Aの場合に限り、Stack[A]Stack[B]の唯一のサブタイプとなります。これでは制限が強いので、ジェネリック型のサブタイプの振る舞いをコントロールするために、Scalaは型引数アノテーションの仕組みを提供します。

Contributors to this page: