Scala 之旅

下界类型

语言

虽然 上界类型 将类型限制为另一类型的子类型,但下界类型将类型声明为另一类型的超类型。术语 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 警告推断出的类型参数是否过于宽泛。

此页面的贡献者