Scala之旅-上边界(UPPER TYPE BOUNDS)和下边界(LOWER TYPE BOUNDS)

上边界

Scala 中,类型参数抽象类型可能受到类型界限的约束。这样的类型边界限制了类型变量的具体值并可能揭示此类类型成员的更多信息。上界 T <: A 声明变量类型 TA 类型的子类型。 这里有个例子演示了 PetContainer 类型参数的上边界。

abstract class Animal {
 def name: String
}

abstract class Pet extends Animal {}

class Cat extends Pet {
  override def name: String = "Cat"
}

class Dog extends Pet {
  override def name: String = "Dog"
}

class Lion extends Animal {
  override def name: String = "Lion"
}

class PetContainer[P <: Pet](p: P) {
  def pet: P = p
}

val dogContainer = new PetContainer[Dog](new Dog)
val catContainer = new PetContainer[Cat](new Cat)
//  val lionContainer = new PetContainer[Lion](new Lion)
//                         ^this would not compile

这里写图片描述
PetContainer 接受的参数类型 P 必须要是 Pet 的子类型。因为 DogCatPet 的子类型,所以我们可以创建一个新的 PetContainer[Dog]PetContainer[Cat]。但是,如果我们尝试创建一个 PetContainer[Lion],我们将会获得以下的错误:type arguments [Lion] do not conform to class PetContainer's type parameter bounds [P <: Pet](类型参数[Lion]不符合 PetContainer's 的类型参数边界 [P <: Pet]
这里写图片描述

下边界

当上边界(upper type bounds)限制一个类型只能是另一个类型的子类型时,下边界(lower type bounds)声明一个类型只能是另一个类型的父类型。术语 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)其余部分引用的节点。因为我们有 +B,所以 class Node 和它的子类型是协变的。

但是,这个程序没有通过编译,因为在 prepend 中的参数 elem 的类型 B 在声明中是协变的。这是不可以的,因为函数中的参数类型是逆变的并且返回类型是协变的

为了解决这个问题,我们需要在 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] 可以被分配给 africanSwallowList 但是接下来方法接受 EuropeanSwallow

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值