我的理解是在以下情况下使用类型差异:
如果泛型类型
G
具有类型参数T1
,它作为G
方法的参数的类型出现,则G
可以在 中是逆变的T1
。如果
G
有类型参数T2
,它显示为 的方法(或 ctor)的任何返回值的类型G
,则G
可以在 中是协变的T2
。
如果我在上面的句子中替换can be with should be怎么办?还有其他使用协变和反变的情况吗?你什么时候以及为什么要让你的类型是协变和反变的?
事情并没有那么简单。有时方差根本没有任何意义,所以你只需保持类不变。
另外,请注意,差异会沿着使用链切换。例如:
class A[+T]
class B[-T] {
def f(x: A[T]) {}
}
class C[+T] {
def g(x: B[T]) {}
}
或者,换一种说法,这不是几行就可以描述的简单的事情。这就是为什么 Scala 强制执行方差是一件非常有用的事情的主要原因——如今,我有一半的理由相信在 Java 中使用方差的大多数代码肯定有细微的错误。
引用规范,第 4.5 节方差注释:
Variance annotations 指示参数化类型的实例如何随着子类型而变化(第 3.5.2 节)。“+”方差表示协变依赖,“-”方差表示逆变依赖,而缺失方差指示表示不变依赖。方差注释限制了带注释的类型变量可能出现在绑定类型参数的类型或类中的方式。在类型定义中,类型 T [tps] = S 或类型声明类型 T [tps] >: L <: U 标记为 +' 的类型参数只能出现在协变位置,而标记为“-”的类型参数只能出现在逆变位置位置。
因此,默认情况下,类型参数被认为是不变的。如果要使用它,则必须显式地将类型参数注释为协变或逆变。此外,在根本不使用的类型参数上使用方差注释是完全合法的(尽管他可能不是那么有用)。例如:
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 的分配合法。如果您不使用方差注释,这将失败。
让我试试这个老问题。协变和逆变的用法之一是通过下界>:(协方差)和上界<:(逆变)对泛型进行一些限制。用法可以在以下代码片段中看到。它来自我自己关于这个主题的博客。
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)