Scala的泛型,上界和下届、协变和逆变

  • Post author:
  • Post category:其他




引子:


为了弄懂scala中协变和逆变这两个概念,查阅了不少资料,但是还是要自己总结一下,会记得比较深刻。


那就从java和scala的对比说起吧。


java中:


如果你很理解java的泛型,就会知道:比如给定一个类B,和他的父类A。


那么用多态, A a = new B 编译器是允许的。


但是如果泛型B的集合直接赋给父类A的集合。List<A> aList = new ArrayList<B>();


举个简单的例子:


Object s = “abc”;


List<Object> objects = new ArrayList<String>();


编译不通过,编译器提示:


Type mismatch: cannot convert from ArrayList<String> to List<Object>



scala中:


我们知道sting是AnyRef的子类。直接赋值是可以的。


如果是将string的集合赋值给AnyRef的集合,在scala中也是可以的。


scala> val s:AnyRef = “abc”


s: AnyRef = abc


scala> var objects : List[AnyRef] = List[String](“abc”,”123″)


objects: List[AnyRef] = List(abc, 123)


原因就在于List其实是支持协变的。



上界和下界

Scala和Java类似,Java中, (? extends T), T称为上界,, 代表T和T的子类, (? supers T), T称为下界。

(1) U >: T


这是类型下界的定义,也就是U必须是类型T的父类(或本身,自己也可以认为是自己的父类)。

(2) S <: T


这是类型上界的定义,也就是S必须是类型T的子类(或本身,自己也可以认为是自己的子类)。


1、定义泛型类的上界

/* 下面的意思就是表示只要是Comparable就可以传递,下面是类上定义的泛型

*/

class GenericTest1[T <: Comparable[T]] {

def choose(one:T,two:T): T ={


//定义一个选择的方法

if(one.compareTo(two) > 0) one else two

}

}

class Boy(val name:String,var age:Int) extends Comparable[Boy]{


override def compareTo(o: Boy): Int = {


this.age – o.age

}

}

object GenericTestOne{


def main(args: Array[String]): Unit = {


val gt = new GenericTest1[Boy]

val huangbo = new Boy(“huangbo”,60)

val xuzheng = new Boy(“xuzheng”,66)

val boy = gt.choose(huangbo,xuzheng)

println(boy.name)

}

}


2、定义泛型方法的上界

class GenericTest2{


//在方法上定义泛型

def choose[T <: Comparable[T]](one:T,two:T): T ={


if(one.compareTo(two) > 0) one else two

}

}

class Boy(val name:String,var age:Int) extends Comparable[Boy]{


override def compareTo(o: Boy): Int = {


this.age – o.age

}

}

object GenericTestTwo{


def main(args: Array[String]): Unit = {


val gt = new GenericTest2

val huangbo = new Boy(“huangbo”,60)

val xuzheng = new Boy(“xuzheng”,66)

val boy = gt.choose(huangbo,xuzheng)

println(boy)

}

}


3


、下界示例

class GranderFather

class Father extends GranderFather

class Son extends Father

class Tongxue

object Card{


def getIDCard[T >: Son](person:T): Unit ={


println(“OK,交给你了”)

}

def main(args: Array[String]): Unit = {


getIDCard(new Father)

getIDCard(new GranderFather)

getIDCard(new Son)

//getIDCard(new Tongxue)//报错,所以注释

}

}


协变和逆变


对于一个带类型参数的类型,比如 C[T],在声明Scala的泛型类型时,“+”表示协变,而“-”表示逆变。



协变


[+T], covariant (or “flexible”) in its type parameter T,类似Java中的(? extends T), 即可以用T和T的子类来替换T,里氏替换原则。


可以看到List的定义:


type List[+A] = scala.collection.immutable.List[A]


协变的符号是[+A],意味着支持泛型A的子类集合向A进行赋值。


不变


[T], invariant  in its type parameter T


在scala可变集合中,MutableList是一个不变的类型。


定义:


class MutableList[A]


extends AbstractSeq[A]


……


我们还用相似的场景,将一个MutableList的string类型付给MutableList的AnyRef类型,这样的赋值是不允许的。


编译器会提示class MutableList is invariant in type A. 即在类型A中MutableList是不支持协变和逆变的。


scala> import scala.collection.mutable._


import scala.collection.mutable._



scala> val a : MutableList[AnyRef] = MutableList[String](“abc”)


<console>:10: error: type mismatch;


found   : scala.collection.mutable.MutableList[String]


required: scala.collection.mutable.MutableList[AnyRef]


Note: String <: AnyRef, but class MutableList is invariant in type A.


You may wish to investigate a wildcard type such as `_ <: AnyRef`. (SLS 3.2.10)


val a : MutableList[AnyRef] = MutableList[String](“abc”)


^


逆变


[-T], contravariant, 类似(? supers T)


if T is a subtype of type S, this would imply that Queue[S] is a subtype of Queue[T]


只能用T的父类来替换T。是逆里氏替换原则。


在  scala.actors.OutputChannel 这个trait是一个逆变的类型。


trait OutputChannel[-Msg] {


……


对于OutputChannel[String], 支持的操作就是输出一个string, 同样OutputChannel[AnyRef]也一定可以支持输出一个string, 因为它支持输出任意一个AnyRef(它要求的比OutputChannel[String]少) 。


但反过来就不行, OutputChannel[String]只能输出String, 显然不能替换OutputChannel[AnyRef]




总结:


协变


[+T], covariant (or “flexible”) in its type parameter T,类似Java中的(? extends T), 即可以用T和T的子类来替换T,里氏替换原则。


不变


不支持T的子类或者父类,只知支持T本身。


逆变


[-T], contravariant, 类似(? supers T) 只能用T的父类来替换T。是逆里氏替换原则。


上界:


只允许T的超类U来替换T。 [U >: T]


下界:


只允许T的子类U来替代T。 [U <: T]


参考:


http://blog.csdn.net/oopsoom/article/details/24773239


https://www.cnblogs.com/qingyunzong/p/8877403.html

>



版权声明:本文为tcl_6666原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。