Scala中Covariant / Contravariant Annotations的使用案例

时间:2015-01-12 22:26:13

标签: scala

在Scala中,可以通过以下方式指定函数或类是协变还是反变量

class Foo[+arg] // covarient
class Bar[-arg] // contravarient

此功能的实际用途是什么? 我知道编译器运行检查以确保所声明的实体实际上是协变的或者其他方式,但是甚至添加这样的注释有什么好处呢?

2 个答案:

答案 0 :(得分:2)

最简单的情况是你可能已经使用它而不知道它是scala集合。

class A()
class B() extends A
case class Container[T](elem : T)

val listOfA:List[A] = List[B](new B(),new B())
val containerOfA:Container[A] = Container[B](new B()) // fails

答案 1 :(得分:0)

想象一下,您有以下层次结构:

class A
class B extends A

<强>协方差即可。协变类型可以用作返回类型:

class Foo[+arg] { // Covariant
  def getArg(): arg = ???
}

def testCovariant(): Unit = {
  val fooB = new Foo[B]
  val foo: Foo[A] = fooB

  // returns only objects of class derived from A
  // so it is safe
  val a: A = foo.getArg()
}

因此,您可以使用Foo[DerivedClass]中使用Foo[BaseClass]的任何一个,因为在Foo[BaseClass].getArg被称为BaseClass的任何地方都会被视为结果,任何DerivedClass都可以返回并分配给它。

<强>逆变即可。 Contravariant类型可用作方法参数类型:

class Bar[-arg] { // Contravariant
  def setArg(p: arg): Unit = ???
}

def testContravariant(): Unit = {
  val barA = new Bar[A]
  val bar: Bar[B] = barA

  // can set B to bar which is actually Bar[A]
  // but Bar[A].setArg(p: A) can accept any object 
  // of type derived from A
  // so it is safe
  bar.setArg(new B)
}

再次。您可以使用Bar[DerivedClass]中使用Bar[BaseClass]的任何一个,因为任何名为Bar[DerivedClass].setArg(p: DerivedClass)的{​​{1}}都需要作为参数,任何DerivedClass都可以在此上下文中使用,因为您始终可以将Bar[BaseClass]传递给DerivedClass