型パラメータ


クラスは0個以上の型をパラメータとして取ることができます。これは、クラスを作る時点で何の型か特定できない 場合(例えば、コレクション要素の型)を表したい時に役に立ちます。 型パラメータには好きな名前を付け、クラス定義の中で使うことができます。

class Cell[A](var value: A) {
  def put(newValue: A): Unit = {
    value = newValue
  }
  def get(): A = value
}
  val cell = new Cell[Int](1)
  println(cell.get())
  cell.put(2)
  println(cell.get())

型パラメータとしてInt型を与えて、その初期値をとして1を与えています。型パラメータにIntを与えて、cellを初期化したため文字列をputしようとするとコンパイルエラーになります。
メソッドから複数の値を返却する場合などに使用できます。
例えば、ただ2つの値を返したい場合には以下のように型パラメータを2つ取るPairクラスを作成します。

class Pair[A, B](val a: A, val b: B) {
  override def toString(): String = "(" + a + "," + b + ")"
}

クラスPairの利用法としては、例えば割り算の商と余りの両方を返却するメソッドdivideがあります。

def divide(m: Int, n: Int): Pair[Int, Int] = new Pair[Int, Int](m / n, m % n)
val div = divide(7, 3)
println(div.toString() )
//Pairの2つの型は異なる型でも問題ありません。
//Scalaでは、このPairのようなクラスがよく使われるため、Tuple1からTuple22(Tupleの後の数字は要素数)が予め用意されています。また、インスタンス化する際も以下のようにできます。
val m = 7
val n = 3
println((m / n, m % n) )

型パラメータの性質である反変、共変、非変について。 非変とは型パラメータを持ったクラスG、型パラメータAとBがありかつ、A=Bのときにのみ G[A] = G[B] という代入が許される性質を表します。 共変とは型パラメータを持ったクラスG、型パラメータAとBがありかつ、AがBを継承しているときにのみ G[B] = G[A] という代入が許される性質を表します。 Scalaでは、クラス定義時に class G[+A] のように型パラメータの前に+を付けるとその型パラメータ(あるいはそのクラス)は共変になります。 Javaの組み込み配列クラスは標準で共変になっています。

Object[] objects = new String[1];
objects[0] = 100;

よってこのJavaコードはコンパイルが通ります、しかし実行するとエラーが発生します。 一方、Scalaでは同様のコードをコンパイルしようとした時点でコンパイルエラーが発生します。

Scalaでは型パラメータを共変にした時点で、安全ではない操作はコンパイルエラーが発生します。 共変がどのような場合に使えるのかというと、例えば先程作成したクラスPair[A, B]のような場合です。
Pair[A, B]は一度インスタンス化したら、変更する操作ができません。よって例外は起こりえません。
実際にPair[A, B]は安全に共変にできるクラスでclass Pair[+A, +B]のようにしても問題が起こりません。

class Pair2[+A, +B](val a: A, val b: B) {
  override def toString(): String = "(" + a + "," + b + ")"
}
val pair2: Pair2[AnyRef, AnyRef] = new Pair2[String, String]("foo", "bar")
println(pair2.toString())

反変とは型パラメータを持ったクラスG、型パラメータAとBがありかつ、AがBを継承しているときにのみ G[A] = G[B] というような代入が許される性質を表します。 Scalaでは、クラス定義時に class G[-A] のように型パラメータの前に-を付けるとその型パラメータは(あるいはそのクラス)反変になります。

val x1: String => AnyRef = (x: AnyRef) => x

これはコンパイルエラーになります。

val x2: AnyRef => AnyRef = (x: String) => (x: AnyRef)