协变/逆变/非变
协变和逆变主要是用来解决参数化类型(Generic)的泛化问题。参数化类型的参数(类型参数)是可变的,当两个参数化类型的参数是继承关系(可泛化),那被参数化的类型在Java中不可泛化,而在Scala中可泛化(协变、逆变和非变)。
-
非变
trait Node[T] {}:当B是A的子类时,Node[B] 不是 Node[A] 的子类
-
协变
trait Node[+T] {}:当B是A的子类时,Node[B] 也是 Node[A] 的子类,Node[B] 可以泛化为Node[A],参数化类型泛化方向和参数类型一致,故称为协变。
-
逆变
trait Node[-T] {}:当B是A的子类时,则相反Node[A]是Node[B}的子类,参数化类型的泛化方向与参数类型相反
scala中参数是逆变的或者不变的,返回值是协变的或者不变的:
class Animal
class Sheep extends Animal
class Cowl extends Animal
- 参数不允许是协变的
因为如果允许参数协变,会出现以下不严格的类型转换class Herd[+T] (val value: ListBuffer[T]){ def add(a: T) = value.append(a) } add方法编译报错:covariant type T occurs in contravariant position
如果必须要在参数中使用协变类型,有以下两种方式val sheepHerd: Herd[Animal] = new Herd[Sheep] //协变 val wolf: Animal = new Wolf sheepHerd.add(wolf) //!!! 在羊群中添加了一只狼
- 使用下边界,引入新的参数类型,将参数类型改为非协变类型
class Herd[+T] (val value: ListBuffer[T] = ListBuffer()){ def add[U >: T] (a: U ) = {} }
- 改为私有方法,避免被不正确访问导致类型转换问题
class Herd[+T] (val value: ListBuffer[T] = ListBuffer()){ priavte(this) def add (a: T ) = {} }
- 使用下边界,引入新的参数类型,将参数类型改为非协变类型
- 返回值不允许逆变
如果允许会出现以下不严格的类型转换:class Herd[-T] (val value: ListBuffer[T] = ListBuffer()){ def choseLeader = value.head } 编译报错:contravariant type T occurs in covariant position
val sheepHerd: Herd[Sheep] = new Herd[Animal]() //逆变 val sheep: Sheep = sheepHerd.choseLeader //!!! sheepHerd的类型实际是:Herd[Animal],因此返回的不一定是Sheep
UPPER/Lower TYPE BOUNDS
上边界 T <: A 声明类型T必须是A的子类
class Dog extends Pet {
override def name: String = "Dog"
}
class PetContainer[P <: Pet](p: P) {
def pet: P = p
}
下边界 B >: A 声明B必须是A的父类