简体   繁体   English

协变类型参数

[英]Covariant type parameter

I have been trying to push my understanding of Scala a little bit further lately and I cannot really figure out some things about covariant/contravariant type parameters. 我最近一直在努力推动我对Scala的理解,我无法弄清楚有关协变/逆变类型参数的一些事情。

Let's say I have a class called Basket as follows : 假设我有一个名为Basket的课程,如下所示:

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

and some classes like this: 和这样的一些类:

trait Fruit
class Banana extends Fruit
class Orange extends Fruit

I know for sure that these assertions are correct : 我肯定知道这些断言是正确的:

  • Basket[Fruit] can be instantiated Basket[Fruit]可以实例化

  • Basket[String] cannot be instantiated (because String is not a subtype of Fruit ) Basket[String]无法实例化(因为String不是Fruit的子类型)

  • Basket[Banana] is a subtype of Basket[Fruit] Basket[Banana]Basket[Fruit]的子类型

  • Basket[Orange] is a subtype of Basket[Fruit] Basket[Orange]Basket[Fruit]的子类型

  • This code: 这段代码:

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

will return a Basket[Fruit] 将返回一个Basket[Fruit]

  • This code: 这段代码:

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

will return a Basket[Banana] 将返回一个Basket[Banana]


What I do not understand is how the B >: A affects the return type of the method .. Why when I add an Orange the return type become Basket[Fruit] and when I add a Banana , it stays a Basket[Banana] ? 我不明白的是B >: A如何影响方法的返回类型。为什么当我添加Orange返回类型变为Basket[Fruit] ,当我添加Banana ,它会保留一个Basket[Banana] Does it look for the "lowest" common super-type ? 它是否寻找“最低”的普通超类型?

Yes, the Scala compiler tries to find the lowest common super-type. 是的,Scala编译器试图找到最低的常见超类型。 You can see it anywhere in Scala, including standard library classes. 您可以在Scala中的任何位置查看它,包括标准库类。 Consider this example for List, which is also covariant on its parameter type: 考虑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"

One can argue this is a dubious feature of Scala type system, because it's easy to end up with something like Any (how it's in my example) or Product with Serializable (if you're dealing with case classes), and then the error messages are quite misleading. 有人可以说这是Scala类型系统的一个可疑特性,因为很容易最终得到类似Any (如何在我的例子中)或Product with Serializable (如果你正在处理case类),然后是错误消息非常误导。

If you want to restrict Scala from generalizing your types, you should use the "sad in a hat" type constraint <:< . 如果您想限制Scala概括您的类型,您应该使用“悲伤的帽子”类型约束<:< Then your code would look like this (I've changed the classes to case classes for readability): 然后你的代码看起来像这样(为了便于阅读,我将类更改为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()))

You can read more about this pattern in an very informative blog post here: http://blog.bruchez.name/2015/11/generalized-type-constraints-in-scala.html 您可以在以下信息丰富的博客文章中阅读有关此模式的更多信息: 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