[英]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.