Tour of Scala

下限型境界

Language

上限型境界 は型を別の型のサブタイプに制限しますが、下限型境界は型が別の型のスーパータイプであることを宣言します。表現B >: AはパラメータBまたは抽象型Bが型Aのスーパータイプであることを表します。ほとんどのケースでAはそのクラスの型パラメータであり、Bはメソッドの型パラメータになります。

以下はこれが役立つ場合の例です。

trait Node[+B] {
  def prepend(elem: B): Node[B]
}

case class ListNode[+B](h: B, t: Node[B]) extends Node[B] {
  def prepend(elem: B): ListNode[B] = ListNode(elem, this)
  def head: B = h
  def tail: Node[B] = t
}

case class Nil[+B]() extends Node[B] {
  def prepend(elem: B): ListNode[B] = ListNode(elem, this)
}

このプログラムは片方向リストを実装します。Nilは空の要素(すなわち空のリスト)を意味します。 class ListNodeは型B (head)の要素と、リストの残りの部分(tail)への参照を持つノードです。 class Nodeとそのサブタイプは、+Bとあるので、共変です。

しかしながら、このプログラムはコンパイル されませんprependのパラメータelemが、宣言時に 変と宣言した型Bになっているからです。 なぜ通らないかというと、関数はそれらの型パラメータに対して変であり、それらの結果型に対して変だからです。

これを解決するためには、prependのパラメータelemの型の変位指定を逆転させる必要があります。 これを実現するには、下限型境界としてBを持つ新しい型パラメータUを導入します。

trait Node[+B] {
  def prepend[U >: B](elem: U): Node[U]
}

case class ListNode[+B](h: B, t: Node[B]) extends Node[B] {
  def prepend[U >: B](elem: U): ListNode[U] = ListNode(elem, this)
  def head: B = h
  def tail: Node[B] = t
}

case class Nil[+B]() extends Node[B] {
  def prepend[U >: B](elem: U): ListNode[U] = ListNode(elem, this)
}

すると、以下のようなことができます。

trait Bird
case class AfricanSwallow() extends Bird
case class EuropeanSwallow() extends Bird


val africanSwallowList= ListNode[AfricanSwallow](AfricanSwallow(), Nil())
val birdList: Node[Bird] = africanSwallowList
birdList.prepend(EuropeanSwallow())

Node[Bird]africanSwallowListをアサインできますが、その後EuropeanSwallowを受け入れられます。

Contributors to this page: