簡體   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