虽然 上界类型 将类型限制为另一类型的子类型,但下界类型将类型声明为另一类型的超类型。术语 B >: A
表示类型参数 B
或抽象类型 B
引用类型 A
的超类型。在大多数情况下,A
将是类的类型参数,B
将是方法的类型参数。
以下是一个有用的示例
trait List[+A] {
def prepend(elem: A): NonEmptyList[A] = NonEmptyList(elem, this)
}
case class NonEmptyList[+A](head: A, tail: List[A]) extends List[A]
object Nil extends List[Nothing]
trait List[+A]:
def prepend(elem: A): NonEmptyList[A] = NonEmptyList(elem, this)
case class NonEmptyList[+A](head: A, tail: List[A]) extends List[A]
object Nil extends List[Nothing]
此程序实现了一个单向链表。 Nil
表示没有元素的空列表。 class NonEmptyList
是一个节点,其中包含类型 A
的元素 (head
) 和对列表其余部分的引用 (tail
)。 trait List
及其子类型是协变的,因为我们有 +A
。
但是,此程序不会编译,因为 prepend
中的参数 elem
是类型 A
,我们声明它为协变。这不起作用,因为函数在其参数类型中是逆变的,在其结果类型中是协变的。
要解决此问题,我们需要翻转 prepend
中参数 elem
的类型方差。我们通过引入一个新的类型参数 B
来实现这一点,它具有 A
作为下界类型。
trait List[+A] {
def prepend[B >: A](elem: B): NonEmptyList[B] = NonEmptyList(elem, this)
}
case class NonEmptyList[+A](head: A, tail: List[A]) extends List[A]
object Nil extends List[Nothing]
trait List[+A]:
def prepend[B >: A](elem: B): NonEmptyList[B] = NonEmptyList(elem, this)
case class NonEmptyList[+A](head: A, tail: List[A]) extends List[A]
object Nil extends List[Nothing]
现在我们可以执行以下操作
trait Bird
case class AfricanSwallow() extends Bird
case class EuropeanSwallow() extends Bird
val africanSwallows: List[AfricanSwallow] = Nil.prepend(AfricanSwallow())
val swallowsFromAntarctica: List[Bird] = Nil
val someBird: Bird = EuropeanSwallow()
// assign swallows to birds
val birds: List[Bird] = africanSwallows
// add some bird to swallows, `B` is `Bird`
val someBirds = africanSwallows.prepend(someBird)
// add a swallow to birds
val moreBirds = birds.prepend(EuropeanSwallow())
// add disparate swallows together, `B` is `Bird` because that is the supertype common to both swallows
val allBirds = africanSwallows.prepend(EuropeanSwallow())
// but this is a mistake! adding a list of birds widens the type arg too much. -Xlint will warn!
val error = moreBirds.prepend(swallowsFromAntarctica) // List[Object]
协变类型参数允许 birds
获取 africanSwallows
的值。
类型参数的类型绑定 prepend
允许添加不同种类的燕子并获取更宽泛的类型:我们获得 List[Bird]
,而不是 List[AfricanSwallow]
。
使用 -Xlint
警告推断出的类型参数是否过于宽泛。