协变、逆变、非变以及上下界
协变、逆变、非变介绍
协变和逆变主要是用来解决参数化类型的泛化问题。Scala的协变与逆变是非常有特色的,完全解决了Java中泛型的一大缺憾;举例来说,Java中,如果有 A是 B的子类,但 Card[A] 却不是 Card[B] 的子类;而 Scala 中,只要灵活使用协变与逆变,就可以解决此类 Java 泛型问题;
由于参数化类型的参数(参数类型)是可变的,当两个参数化类型的参数是继承关系(可泛化),在Java中这种情况下,那被参数化的类型是不可泛化的,然而Scala提供了三个选择,即协变(“+”)、逆变(“-”)和非变。
下面说一下三种情况的含义,首先假设有参数化特征Queue,那它可以有如下三种定义。
(1) trait Queue[T] {}
这是非变情况。这种情况下,当类型B是类型A的子类型,则Queue[B]与Queue[A]没有任何从属关系,这种情况是和Java一样的。
(2) trait Queue[+T] {}
这是协变情况。这种情况下,当类型B是类型A的子类型,则Queue[B]也可以认为是Queue[A]的子类型,即Queue[B]可以泛化为Queue[A]。也就是被参数化类型的泛化方向与参数类型的方向是一致的,所以称为协变。
(3) trait Queue[-T] {}
这是逆变情况。这种情况下,当类型B是类型A的子类型,则Queue[A]反过来可以认为是Queue[B]的子类型。也就是被参数化类型的泛化方向与参数类型的方向是相反的,所以称为逆变。
总结:
C[+T]:如果A是B的子类,那么C[A]是C[B]的子类。
C[-T]:如果A是B的子类,那么C[B]是C[A]的子类。
C[T]: 无论A和B是什么关系,C[A]和C[B]没有从属关系。
class Dog
class Xiaogou extends Dog
class Animals[+T](name: String)//协变
class Animals2[-T](name:String)//逆变
class Animals3[T](name:String)//非变
object Test{
def main(args: Array[String]): Unit = {
val a1:Animals[Dog]=new Animals[Xiaogou]("小狗是Dog")
//val a2:Animals2[Dog]=new Animals2[Xiaogou]("小狗报错啦")
val a2:Animals2[Xiaogou]=new Animals2[Dog]("Dog是小狗")
//type mismatch,编译报错
//val a3:Animals3[Xiaogou]=new Animals3[Dog]("xiaogou又报错了")
}
}
上界、下界介绍
在指定泛型类型时,有时需要界定泛型类型的范围,而不是接收任意类型。
例如,要求某个泛型类型,必须是某个类的子类,这样在程序中就可以放心的调用父类的方法,程序才能正常的使用与运行。此时,就可以使用上下边界Bounds的特性;
Scala的上下边界特性允许泛型类型是某个类的子类,或者是某个类的父类;
(1) U >: T ? super T
这是类型下界的定义,也就是U必须是类型T的父类(或本身,自己也可以认为是自己的父类)。
(2) S <: T ? extends T
这是类型上界的定义,也就是S必须是类型T的子类(或本身,自己也可以认为是自己的子类)。