繁体   English   中英

协变类型参数

[英]Covariant type parameter

我最近一直在努力推动我对Scala的理解,我无法弄清楚有关协变/逆变类型参数的一些事情。

假设我有一个名为Basket的课程,如下所示:

class Basket[+A <: Fruit](items: List[A]) {
  // ...
  def addAll[B >: A <: Fruit](newItems: List[B]): Basket[B] =
new Basket(items ++ newItems)
  // ...
}

和这样的一些类:

trait Fruit
class Banana extends Fruit
class Orange extends Fruit

我肯定知道这些断言是正确的:

  • Basket[Fruit]可以实例化

  • Basket[String]无法实例化(因为String不是Fruit的子类型)

  • Basket[Banana]Basket[Fruit]的子类型

  • Basket[Orange]Basket[Fruit]的子类型

  • 这段代码:

     val bananaBasket: Basket[Banana] = new Basket(List(new Banana, new Banana)) bananaBasket.addAll(List(new Orange)) 

将返回一个Basket[Fruit]

  • 这段代码:

     val bananaBasket: Basket[Banana] = new Basket(List(new Banana, new Banana)) bananaBasket.addAll(List(new Banana)) 

将返回一个Basket[Banana]


我不明白的是B >: A如何影响方法的返回类型。为什么当我添加Orange返回类型变为Basket[Fruit] ,当我添加Banana ,它会保留一个Basket[Banana] 它是否寻找“最低”的普通超类型?

是的,Scala编译器试图找到最低的常见超类型。 您可以在Scala中的任何位置查看它,包括标准库类。 考虑List的这个例子,它的参数类型也是协变的:

1.0 :: List(1, 2, 3)
// result type is AnyVal, least common ancestor of Double and Int
res1: List[AnyVal] = List(1.0, 1, 2, 3)

"0" :: List(1, 2, 3)
// result type is List[Any], lowest common ancestor of String and Int
res2: List[Any] = List(0, 1, 2, 3)

0 :: List(1, 2, 3)
// result type is List[Int] exactly
res3: List[Int] = List(0, 1, 2, 3)

res2.head
res4: Any = 0

res2.head.asInstanceOf[String]
res5: String = "0"

有人可以说这是Scala类型系统的一个可疑特性,因为很容易最终得到类似Any (如何在我的例子中)或Product with Serializable (如果你正在处理case类),然后是错误消息非常误导。

如果您想限制Scala概括您的类型,您应该使用“悲伤的帽子”类型约束<:< 然后你的代码看起来像这样(为了便于阅读,我将类更改为case类):

case class Banana() extends Fruit
defined class Banana

case class Orange() extends Fruit
defined class Orange

case class Basket[+A <: Fruit](items: List[A]) {
    // ...
    def addAll[B >: A <: Fruit, C >: A](newItems: List[B])(implicit ev: B <:< C): Basket[B] =
  new Basket(items ++ newItems)
    // ...
  }
defined class Basket

val bananaBasket: Basket[Banana] = Basket(List(Banana(), Banana()))
bananaBasket: Basket[Banana] = Basket(List(Banana(), Banana()))

bananaBasket.addAll(List(Orange())) // not accepted
Main.scala:593: Cannot prove that Product with Serializable with cmd27.Fruit <:< cmd47.Banana.
bananaBasket.addAll(List(Orange()))
                   ^
Compilation Failed

bananaBasket.addAll(List(Banana())) // accepted
res52: Basket[Banana] = Basket(List(Banana(), Banana(), Banana()))

您可以在以下信息丰富的博客文章中阅读有关此模式的更多信息: http//blog.bruchez.name/2015/11/generalized-type-constraints-in-scala.html

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM