转自:http://fineqtbull.iteye.com/blog/477994#bc2364938app
有位je上的同窗来短信向我问起了Scala类型参数中协变、逆变、类型上界和类型下界的使用方法和原理,本身虽然也刚学不久,在主要调查了《Programing in Scala》的19章后,试着在下面作一个总结。若有错误之处还请各位指正。
先说说协变和逆变(实际上还有非变)。协变和逆变主要是用来解决参数化类型的泛化问题。因为参数化类型的参数(参数类型)是可变的,当两个参数化类型的参数是继承关系(可泛化),那被参数化的类型是否也能够泛化呢?Java中这种状况下是不可泛化的,然而Scala提供了三个选择,即协变、逆变和非变。下面说一下三种状况的含义,首先假设有参数化特征Queue,那它能够有以下三种定义。
1)trait Queue[T] {}
这是非变状况。这种状况下,当类型S是类型A的子类型,则Queue[S]不可认为是Queue[A]的子类型或父类型,这种状况是和Java同样的。
2)trait Queue[+T] {}
这是协变状况。这种状况下,当类型S是类型A的子类型,则Queue[S]也能够认为是Queue[A}的子类型,即Queue[S]能够泛化为Queue[A]。也就是被参数化类型的泛化方向与参数类型的方向是一致的,因此称为协变。
3)trait Queue[-T] {}
这是逆变状况。这种状况下,当类型S是类型A的子类型,则Queue[A]反过来能够认为是Queue[S}的子类型。也就是被参数化类型的泛化方向与参数类型的方向是相反的,因此称为逆变。
接着看一个例子。函数
上例的Library单例对象的printBookList方法使用了函数来取得书籍的内容。在Scala中函数也是对象,上述状况下的函数有一个参数,实际上该参数是以下特征的实例。spa
printBookList的info参数是Function1类型,而 Function1的-S类型参数是逆变,+T参数是协变。printBookList方法的assert(info.isInstanceOf[Function1[_, _]])语句能够验证这一点。从printBookList方法的定义能够知道,info的S类型参数是Book,T类型参数是AnyRef。然而主函数中使用处则是Library.printBookList(getTitle),getTitle函数中对应的S是Publication,T是String。为何能够与printBookList原来的定义不一致呢,这就是协变和逆变的威力了。因为-S是逆变,而Publication是Book的父类,因此Publication能够代替(泛化为)Book。因为+T是协变,而String是AnyRef的子类,因此String能够代替(泛化为)AnyRef。如此一来,主程序的语句也就彻底正确了。scala
接下来讲说类型的上界和下界,它们的含义以下。对象
1) U >: Tblog
这是类型下界的定义,也就是U必须是类型T的父类(或自己,本身也能够认为是本身的父类)。继承
2) S <: Tget
这是类型上界的定义,也就是S必须是类型T的子类(或自己,本身也能够认为是本身的子类)。qt
接着使用前面的例子来讲明>:和<:的使用方法。printBokkListByTrait方法实现了与printBookList相同的功能,但它是经过传入特征对象来实现的。也就是说,new GetInfoAction[Publication, String] {}和def getTitle(p: Publication): String是等价的,而GetInfoAction定义中使用>:和<:来代替了Function1中+和-。那是因为>:使得Publication能够代替Book,因为<:使得String能够代替AnyRef。string
那么为何Function1中的S是逆变而T是协变呢,那是由apply方法的格式而起的。apply方法的参数类型是S决定了S必定是逆变,而返回类型是T则决定了T是协变,这也是Scala语言的强制规定。
咱们再来刨根问底一下,那么为何Scala要有这种规定呢?这实际上和Liskov代替原理有关,它规定T类型是U类型的子类条件是,在U对象出现的全部地方均可以用T对象来代替。同时对于U和T中相同的方法定义,还必须保证T的参数类型需求的比较少,而T的返回类型提供得比较多。从本文的类子来看,参数类型Publication是Book的父类,因此需求的就比Book少;而返回类型String是AnyRef的子类,所提供的就比AnyRef多。以上就是def getTitle(p: Publication): String能够替代info: Book => AnyRef的缘由,也是Scala定义协变和逆变规则的理论基础。
欢迎来Scala圈子看看。
http://scalagroup.group.iteye.com/