转自:http://fineqtbull.iteye.com/blog/477994#bc2364938

有位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}的子类型。也就是被参数化类型的泛化方向与参数类型的方向是相反的,所以称为逆变。

接着看一个例子。

  1. package fineqtbull.customer
  2. //出版物类
  3. class Publication(val title: String)
  4. //书籍类
  5. class Book(title: String) extends Publication(title)
  6. //图书库类
  7. object Library {
  8. //定义图书库内所有的书籍
  9. val books: Set[Book] =
  10. Set(
  11. new Book("Programming in Scala"),
  12. new Book("Walden")
  13. )
  14. //打印所有图书内容,使用外部传入的函数来实现
  15. def printBookList(info: Book => AnyRef) {
  16. //确认Scala中一个参数的函数实际上是Function1特征的实例
  17. assert(info.isInstanceOf[Function1[_, _]])
  18. //打印
  19. for (book <- books)
  20. println(info(book))
  21. }
  22. //打印所有图书内容,使用外部传入的GetInfoAction特征的实例来实现
  23. def printBokkListByTrait[P >: Book, R <: AnyRef](
  24. action : GetInfoAction[P, R]) {
  25. //打印
  26. for (book <- books)
  27. println(action(book))
  28. }
  29. }
  30. //取得图书内容特征,P类型参数的类型下界是Book,R类型参数的类型上界是AnyRef
  31. trait GetInfoAction[P >: Book, R <: AnyRef] {
  32. //取得图书内容的文本描述,对应()操作符
  33. def apply(book : P) : R
  34. }
  35. //单例对象,文件的主程序
  36. object Customer extends Application {
  37. //定义取得出版物标题的函数
  38. def getTitle(p: Publication): String = p.title
  39. //使用函数来打印
  40. Library.printBookList(getTitle)
  41. //使用特征GetInfoAction的实例来打印
  42. Library.printBokkListByTrait(new GetInfoAction[Publication, String] {
  43. def apply(p: Publication) : String = p.title })
  44. }

上例的Library单例对象的printBookList方法使用了函数来取得书籍的内容。在Scala中函数也是对象,上述情况下的函数有一个参数,实际上该参数是如下特征的实例。

  1. trait Function1[-S, +T] {
  2. def apply(x: S): T
  3. }

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。如此一来,主程序的语句也就完全正确了。

接下来说说类型的上界和下界,它们的含义如下。

1) U >: T

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

2) S <: T

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

接着使用前面的例子来说明>:和<:的使用方法。printBokkListByTrait方法实现了与printBookList相同的功能,但它是通过传入特征对象来实现的。也就是说,new GetInfoAction[Publication, String] {}和def getTitle(p: Publication): String是等价的,而GetInfoAction定义中使用>:和<:来代替了Function1中+和-。那是由于>:使得Publication可以代替Book,由于<:使得String可以代替AnyRef。

那么为什么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/

Scala类型参数中协变(+)、逆变(-)、类型上界(<:)和类型下界(>:)的使用的更多相关文章

  1. Programming In Scala笔记-第十九章、类型参数,协变逆变,上界下界

    本章主要讲Scala中的类型参数化.本章主要分成三个部分,第一部分实现一个函数式队列的数据结构,第二部分实现该结构的内部细节,最后一个部分解释其中的关键知识点.接下来的实例中将该函数式队列命名为Que ...

  2. scala学习笔记-类型参数中协变(+)、逆变(-)、类型上界(<:)和类型下界(>:)的使用

    转载自  fineqtbull   http://fineqtbull.iteye.com/blog/477994 有位je上的同学来短信向我问起了Scala类型参数中协变.逆变.类型上界和类型下界的 ...

  3. Scala中的协变,逆变,上界,下界等

    Scala中的协变,逆变,上界,下界等 目录 [−] Java中的协变和逆变 Scala的协变 Scala的逆变 下界lower bounds 上界upper bounds 综合协变,逆变,上界,下界 ...

  4. (转)Scala中协变(+)、逆变(-)、上界(<:)、下界(>:)简单介绍

    看源码的时候看到: trait ExtensionId[T <: Extension] { 没见过这个符号啊<: Scala上界(<:)和下界(>:) 1) U >: T ...

  5. Java中的逆变与协变

    看下面一段代码 Number num = new Integer(1); ArrayList<Number> list = new ArrayList<Integer>(); ...

  6. Java中的逆变与协变(转)

    看下面一段代码 Number num = new Integer(1); ArrayList<Number> list = new ArrayList<Integer>(); ...

  7. OOP中的逆变和协变

    逆变和协变在存在于强类型语言中,尽管非常少提及.可是里面蕴含了面向对象的世界观.感谢和我一起讨论这个问题的人. 这里用了C#.Scala的语法作为演示样例.事实上逆变和协变的概念跟语言本身关系不大.事 ...

  8. C#中泛型方法与泛型接口 C#泛型接口 List<IAll> arssr = new List<IAll>(); interface IPerson<T> c# List<接口>小技巧 泛型接口协变逆变的几个问题

    http://blog.csdn.net/aladdinty/article/details/3486532 using System; using System.Collections.Generi ...

  9. Java中的逆变与协变 专题

    结论先行: PECS总结: 要从泛型类取数据时,用extends: 协变 要往泛型类写数据时,用super: 逆变 既要取又要写,就不用通配符(即extends与super都不用) 不变 List&l ...

随机推荐

  1. nsstring 替换

    -(NSString*)searchaAndPlacing:(NSString*)string { NSString *text=[string stringByReplacingOccurrence ...

  2. C# 利用file打印日志

    public class FaceLog { public static void AppendInfoLog(string errMsg) { try { string Folder = Main. ...

  3. Poem: Reserverd Words

    Let this long package float, Goto private class if short. While protected with debugger case, Contin ...

  4. HDU 1017 - A Mathematical Curiosity

    题目简单,格式酸爽.. #include <iostream> using namespace std; int ans,n,m,p; int main() { cin>>p; ...

  5. 实现FileCopy(Ring0 x86 x64)

    1.1 内核不接受一个字符串文件路径,必须填写一个OBJECT_ATTRIBUTES结构. 这个结构,在InitializeObjectAttributes初始化 typedef struct _OB ...

  6. .Net之托管堆资源分配

    托管堆分配资源: 一:进程初始化是,CLR要保留一块联系的地址空间,这个地址空间最初并没有对应的物理存储空间.这个地址空间就是托管堆.托管堆还维护着一个指针,我把它称为NextObjPtr.它指向下个 ...

  7. Jsoup小Demo

    Jsoup小Demo public class JsoupUtil { public void parseWangYi() { Document doc = null; try { //eg1:解析百 ...

  8. 了解神奇的this

    this的用法 在函数中this到底取何值,是在函数真正被调用执行的时候确定的,函数定义的时候确定不了. 因为this的取值是执行上下文环境的一部分,每次调用函数,都会产生一个新的执行上下环境.举例说 ...

  9. EEPROM和flash的区别

    存储器分为两大类:ram和rom.ram就不讲了,今天主要讨论rom. rom最初不能编程,出厂什么内容就永远什么内容,不灵活.后来出现了prom,可以自己写入一次,要是写错了,只能换一片,自认倒霉. ...

  10. 为什么国外程序员爱用苹果 Mac 电脑?

      Mac 在国外很受欢迎,尤其是在 设计/web开发/IT 人员圈子里.普通用户喜欢 Mac 可以理解,毕竟 Mac 设计美观,简单好用,没有病毒.那么为什么专业人士也对 Mac 情有独钟呢?从个人 ...