Scala通用:具有下限类型的方法的返回数据类型

时间:2018-09-07 06:12:54

标签: scala generics

我已经定义了一些Scala类:

class Drink
class SoftDrink extends Drink
class Cola extends SoftDrink

class VendingMachine[A](val currentItem: Option[A], items: List[A]) {
  def this(items: List[A]) = this(None, items)

  def addAll[B >: A](newItems: List[B]): VendingMachine[B] =
    new VendingMachine(items ++ newItems)
}

然后我运行了以下代码段:

val colasVM: VendingMachine[Cola] = new VendingMachine(List(new Cola, new Cola))
// It works
val softDrinksVM: VendingMachine[Drink] = colasVM.addAll(List(new SoftDrink))
// Compile Error: You may wish to define A as +A instead. (SLS 4.5)
val softDrinksVM2: VendingMachine[Drink] = new VendingMachine[SoftDrink](None, null) 

我认为colasVM.addAll(List(new SoftDrink))返回VendingMachine[SoftDrink]类型的数据,由于它们不是同一类型,因此无法将其分配给VendingMachine[Drink]变量。

但是val softDrinksVM: VendingMachine[Drink] = colasVM.addAll(List(new SoftDrink))可以在我这边成功编译,有人可以帮忙解释一下原因吗?

非常感谢!

1 个答案:

答案 0 :(得分:1)

发生这种情况的原因是下界类型,类型推断和协方差。

colasVM是VendingMachine [Cola],因此其类型参数A是Cola。

方法addAll的类型参数B是A的任何超类型。如果A是可乐,则B可以是Cola,SoftDrink,Drink,甚至可以是AnyRef或Any。

当您调用addAll时,您并没有告诉编译器哪种类型是B,因此它必须进行推断。如果softDrinksVM2的类型为VendingMachine [Drink],则B必须为Drink。

为什么您的代码可以编译?因为List是协变的,所以List [SoftDrink]是List [Drink]。

如您所见,甚至可以做这样的事情。

val softDrinksVM1: VendingMachine[AnyRef] = colasVM.addAll(List(new Object))
val softDrinksVM2: VendingMachine[AnyRef] = colasVM.addAll(List(new SoftDrink))

查看此链接以获取更多信息 https://docs.scala-lang.org/tour/variances.html