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