当类型上限限制了一个类型是另一个类型的子类时,类型下限则声明了一个类型是另一个类型的父类。这个表达式 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
类型元素(头)的结点,而且指向列表的剩下的部分(尾)。 class Node
和它的子类因为有 +B
注解所以是协变。
然而,这个程序并不能通过编译因为在 prepend
方法中的参数 elem
的类型是声明为协变的 B
类型,也不能工作因为函数的参数类型是逆变的且返回类型才是协变的。
为了修复这个BUG,我们需要翻转 prepend
方法中的 elem
参数的变性。通过一个新的类型参数 U
作为 B
的类型下限来实现它。
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(new EuropeanSwallow())
Node[bird]
被 africanSwallow
赋值,但是可以接受 EuropeanSwallow
的实例。