协方差对我来说非常直观,但我对矛盾方差的理解有点不稳定。我理解Function[-A, +B]
中的反差异,因为Animal => Int
函数可以在使用Rabbit => Int
函数的任何地方使用。我真的不明白这种相反的变异关系如何适用于Builder
的{{1}}字词,特别是为什么Elem
' s Builder
是反变体,但Elem
TraversableLike
是协变的。
答案 0 :(得分:0)
让我们从构建者开始,正如文档所说:
所有建造者的基本特征。构建器允许通过使用+ =向构建器添加元素,然后使用结果转换为所需的集合类型,逐步构造集合。
假设我们有一个以Builder为参数的函数。
def addCatAndBuild(builder : Builder[Cat, Buffer]) = {
builder += new Cat()
builder.result()
}
我们能够在这里使用的唯一构建器是那些具有超类型Cat指定为Elem参数的构建器(您可以将Cat添加到任何Animal集合中)。可以在这里使用Builder [Animal,Buffer],但不能使用Builder [ThreeLeggedCat,Buffer](因为不是每只猫只有三条腿)。
至于TraversableLike - 这个特性没有Elem(或A,如scaladoc)实例参数的方法。有接受Elem =>的方法。无论什么功能。一些方法返回Elem的其他集合(例如,List [Elem]),并且所有这些集合都具有协变类型参数。
就TraversableLike的操作而言,您在TraversableLike [Animal,Repr]上所做的任何事情也可以在TraversableLike [Cat,Repr]上完成。
def allCanWalk(tl : TraversableLike[Animal, Repr]) =
tl.forall(_.canWalk)
总结一下,它是类的接口,它的类型参数的使用允许它们是协变的或逆变的。
答案 1 :(得分:0)
有一条经验法则:
对比方差用于函数参数,方差用于函数返回类型
我将详细说明这一点。通常,如果你有这样的功能:
def f(a: A): B
您可以将所有子类型A 的任何实例传递给此函数,并将结果分配给任何超类型B <的类型值/ p>
因此,假设您有以下通用类型:
trait Builder [Elem,To]
这意味着您可以添加Elem
类型的元素以形成类型To
的实例。例如,具有以下类型:
class Animal
class Cat extends Animal
class Dog extends Animal
class Tiger extends Cat
val a: Builder[Cat, ArrayBuffer[Cat]]
为您提供构建器,以构建包含ArrayBuffer
类型元素的Cat
。我们想要的是更通用的结构。我们来看看下面的例子:
implicit val builder: Builder[Cat, ArrayBuffer[Cat]] = ...
def build(elements: Tiger*)(implicit b: Builder[Tiger, ArrayBuffer[Animal]]): ArrayBuffer[Animal] = { ... }
我们有一个构建函数,它需要在作用域中隐含一个ArrayBuffer[Animal]
实例的Tiger
。让我们回到一些逻辑。
ArrayBuffer[Cat]
分配给类型为ArrayBuffer[Animal]
的值是合乎逻辑的。 Tiger
添加到ArrayBuffer[Cat]
是合乎逻辑的,因为Tiger有点像猫。因此,我们的build
函数在逻辑上应该能够使用builder
隐式val来形成ArrayBuffer [Cat]或更一般地从给定的Tiger
个实例集合中形成ArrayBufer [Animal] 。只有在保持以下关系时才可以这样做:
Builder[Cat, ArrayBuffer[Cat]] <: Builder[Tiger, ArrayBuffer[Animal]]
在这种情况下,builder
可以在调用b
函数时选择build
参数。
实现此关系的唯一方法是将Builder特征更改为以下内容:
trait Builder[-Elem, +To]
自Tiger <: Cat
和Cat <: Animal
以来,根据Elem
的反差异和To
上的差异,Builder[Cat, ArrayBuffer[Cat]] <: Builder[Tiger, ArrayBuffer[Animal]]
。
回到介绍性的经验法则:您希望使用Elem
类型的元素来形成类型To
的集合。将它想象为一个函数,Elem
是参数类型,To
是返回类型。因此,返回类型的参数类型和方差的反差异使其成为逻辑上正确的通用函数。