Scala 中 Covarient/Contravarient 注释的用例

Use cases of Covarient/Contravarient Annotations in Scala

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

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

此功能的实际用途是什么? 我知道编译器运行检查以确保声明的实体实际上是协变的或其他,但即使添加此类注释有什么好处?

最简单的情况是您可能已经在使用它而不知道它是 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

假设您有以下层次结构:

class A
class B extends A

协方差。协变类型可以作为return类型:

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可以 returned 并分配给它。

逆变。逆变类型可以作为方法参数类型:

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[BaseClass] 的地方使用 Bar[DerivedClass] 中的任何一个,因为任何 Bar[DerivedClass].setArg(p: DerivedClass) 被称为 DerivedClass 的地方都应该作为参数,并且任何 Bar[BaseClass] 都可以在此上下文中使用, 因为你总是可以将 DerivedClass 传递给 Bar[BaseClass].setArg(p: BaseClass).