除输入/输出参数外,类型方差的其他用法是什么?

时间:2011-03-11 11:22:10

标签: scala variance

我的理解是在以下情况下使用类型差异

  • 如果泛型类型G具有类型参数T1,它显示为G方法的参数类型,则G可以< T1中的强>逆变。

  • 如果G的类型参数T2显示为G的方法(或ctor)的任何返回值的类型,那么G可以在T2协变

如果我在上面的句子中用替换 该怎么办?怎么办?是否有其他使用共同变体和反变体的情况?何时以及为什么要将您的类型变为共同变体和反变体?

3 个答案:

答案 0 :(得分:3)

从规范引用,4.5方差注释:

  

方差注释指示参数化类型的实例如何变化   到子类型(§3.5.2)。 '+'方差表示协变依赖,' - '   方差表示逆变依赖性,缺少方差指示   表示不变的依赖关系。   方差注释约束了带注释的类型变量的显示方式   在绑定类型参数的类型或类中。在类型定义中   类型T [tps] = S,或类型声明类型T [tps]&gt;:L&lt ;:U类型参数   标记为+'必须仅出现在协变位置,而标记的类型参数   ' - '必须只出现在逆变位置。

因此,默认情况下,类型参数被认为是不变的。您必须明确地将类型参数注释为co-or contravariant 如果你想使用它。此外,在完全没有使用的类型参数上使用方差注释是完全合法的(尽管他可能不那么有用)。 例如:


scala> class A[+T, -S] {def myMethod(s: String) = println(s)}
defined class A

scala> class A2[T] {def myMethod(t: T) = println(t)}
defined class A2

scala> class A3[-T] {def myMethod(t: T) = println(t)}
defined class A3

scala> val a1 = new A2[Any]
a1: A2[Any] = A2@1cd1cea

scala> val a2: A2[Int] = a1
:6: error: type mismatch;
 found   : A2[Any]
 required: A2[Int]
       val a2: A2[Int] = new A2[Any]

scala> val a3  = new A3[Any]
a3: A3[Any] = A3@875dee

scala> val a4: A3[Int] = a3
a5: A3[Int] = A3@875dee

A3类中的方差注释(在此示例中是逆变的)使得A3 [Any]被认为是A3 [Int]的子类型, 使从实例a4到a3的分配合法化。如果不使用方差注释,则会失败。

答案 1 :(得分:3)

事情并非那么简单。有时方差根本没有意义,所以你只需保持类不变。

另外,请注意方差在使用链中切换。例如:

class A[+T]
class B[-T] {
  def f(x: A[T]) {}
}

class C[+T] {
  def g(x: B[T]) {}
}

或者换句话说,这可以用几行来描述,这不是一件简单的事情。这就是为什么Scala强有力的方差实施是一个非常有用的东西的主要原因 - 现在,我有一半确信大多数使用Java中的差异的代码必须有微妙的错误。

答案 2 :(得分:1)

让我试一试这个老问题。协方差和逆变的一个用法是通过下限> :(协方差)和上限&lt; :(逆变)对Generic进行一些限制。用法可以在以下代码段中看到。它来自我自己的blog关于这个主题。

    abstract class Animal (animalType:String)

    class HasFourLegs(animalType:String) extends Animal(animalType){
      def move=println(this+" walking on four legs")
    }

    class HasTwoLegs(animalType:String) extends Animal(animalType){
      def move=println(this+" walking on Two legs")
    }

    case class Dog(animalType:String) extends HasFourLegs(animalType)
    case class Ostrich(animalType:String) extends HasTwoLegs(animalType)

      def moveOn4legs[T<:HasFourLegs](animal:T)=  animal.move
      val dog = Dog("dog")
      val ostrich=Ostrich("ostrich")
      moveOn4legs(dog)
      /*
      moveOn4legs(ostrich)
      error: inferred type arguments [this.Ostrich] do not conform to method moveOn4legs's type parameter bounds [T <: this.HasFourLegs]
      moveOn4legs(ostrich)
      ^
      */
      println

    class AnimalMovement [+T]{
      def movement[U>:T](animal:U)=println(animal+" walking on Two legs!!!")
    }

      val moveLikeTwoLegs=new AnimalMovement[HasTwoLegs]()
      moveLikeTwoLegs.movement(ostrich)
      moveLikeTwoLegs.movement(dog)