型パラメータの境界
型パラメータTに対して何も指定しない場合、その型パラメータTはどんな型でも入り得ることしかわかりません。
そのため、型パラメータTに対して呼び出せるメソッドはAnyに対するもののみとなります。
しかし、たとえば、順序がある要素からなるリストをソートしたい場合など、Tに対して制約をかけると便利な場合があります。
そのような場合に使えるのが、型パラメータの境界です。境界には2種類あります。
1つめは、型パラメータがどのような型を継承しているかを指定する上限境界です。
上限境界では、型パラメータの後ろに、<:
を記述し、それに続いて制約となる型を記述します。
以下では、showによって文字列化できるクラスShowを定義した上で、Showであるような型のみを要素として持つShowablePairを定義しています。
abstract class Show {
def show: String
}
class ShowablePair[A <: Show, B <: Show](val a: A, val b: B) extends Show {
//a.show b.showの示すshowメソッドは引数として渡されたShowクラスに付属しているshowメソッドのことである。
override def show: String = "(" + a.show + "," + b.show + ")"
}
class Str1 extends Show {
override def show: String = "hoge"
}
class Str2 extends Show {
override def show: String = "bar"
}
println((new ShowablePair[Show, Show]((new Str1), (new Str2))).show)
2つめは、型パラメータがどのような型のスーパータイプであるかを指定する下限境界です。下限境界は共変パラメータと共に用いることが多い機能です。
イミュータブルなStackクラスを定義します。
abstract class Stack[+A] {
//この定義はコンパイルエラーになります。
//一般に、引数の位置に共変型パラメータEの値が来た場合、型安全性が壊れる可能性があるため、コンパイルエラーになります。
//しかし、このStackは配列と違ってイミュータブルであるため、本来なら型安全性上の問題は起きません。
//この問題に対処するために型パラメータの下限境界を使うことができます。型パラメータEをpushに追加し、その下限境界として、Stackの型パラメータAを指定します。
//def push(element: A): Stack[A]
def push[E >: A](element: E): Stack[E]
def top: A
def pop: Stack[A]
def isEmpty: Boolean
}
このようにすることで、コンパイラはStackにはAの任意のスーパータイプの値が入れられる可能性があることがわかるようになります。
そして、型パラメータEは共編では無いため、どこに出現しても構いません。このようにして、下限境界を利用して、型安全なStackと共変性を両立することができます。