scala-协变、逆变、上界、下界

当我们定义一个协变类型List[A+]时,List[Child]可以是List[Parent]的子类型。

当我们定义一个逆变类型List[-A]时,List[Child]可以是List[Parent]的父类型。

Scala的协变

看下面的例子:

    class Animal {}
class Bird extends Animal {}
class Animal {}
class Bird extends Animal {}
//协变
class Covariant[T](t:T){}
val cov = new Covariant[Bird](new Bird)
val cov2:Covariant[Animal] = cov

c不能赋值给c2,因为Covariant定义成不变类型。

稍微改一下:

    class Animal {}
class Bird extends Animal {}
class Animal {}
class Bird extends Animal {}
//协变
class Covariant[+T](t:T){}
val cov = new Covariant[Bird](new Bird)
val cov2:Covariant[Animal] = cov

因为Consumer定义成协变类型的,所以Covariant[Bird]是Covariant[Animal]的子类型,所以它可以被赋值给c2。

Scala的逆变

将上面的例子改一下:

    class Animal {}
class Bird extends Animal {}
class Contravariant[-T](t: T) {
}
val c: Contravariant[Animal] = new Contravariant[Animal](new Animal)
val c2: Contravariant[Bird] = c

这里Consumer[-T]定义成逆变类型,所以Contravariant[Animal]被看作Contravariant[Animal]的子类型,故c可以被赋值给c2。

下界lower bounds

如果协变类包含带类型参数的方法时:

    class Animal {}
class Bird extends Animal {}
class Consumer[+T](t: T) {
def use(t: T) = {}
}

编译会出错。出错信息为 "Covariant type T occurs in contravariant position in type T of value t"。
但是如果返回结果为类型参数则没有问题。

    class Animal {}
class Bird extends Animal {}
class Consumer[+T](t: T) {
def get(): T = {new T}
}

为了在方法的参数中使用类型参数,你需要定义下界:

    class Animal {}
class Bird extends Animal {}
class Consumer[+T](t: T) {
def use[U >: T](u : U) = {println(u)}
}

这个地方比较复杂, 简单的说就是Scala内部实现是, 把类中的每个可以放类型的地方都做了分类(+, –, 中立), 具体分类规则不说了 对于这里最外层类[+T]是协变, 但是到了方法的类型参数时, 该位置发生了翻转, 成为-逆变的位置, 所以你把T给他, 就会报错说你把一个协变类型放到了一个逆变的位置上

所以这里的处理的方法就是, 他要逆变, 就给他个逆变, 使用[U >: T], 其中T为下界, 表示T或T的超类, 这样Scala编译器就不报错了

上界upper bounds

看一下逆变类中使用上界的例子:

    class Animal {}
class Bird extends Animal {}
class Consumer[-T](t: T) {
def get[U <: T](): U = {new U}
}

可以看到方法的返回值是协变的位置,方法的参数是逆变的位置。
因此协变类的类型参数可以用在方法的返回值的类型,在方法的参数类型上必须使用下界绑定 >:。
逆变类的类型参数可以用在方法的参数类型上,用做方法的返回值类型时必须使用上界绑定 <:。

综合协变,逆变,上界,下界

一个综合例子:

    class Animal {}
class Bird extends Animal {}
class Consumer[-S,+T]() {
def m1[U >: T](u: U): T = {new T} //协变,下界
def m2[U <: S](s: S): U = {new U} //逆变,上界
}
class Test extends App {
val c:Consumer[Animal,Bird] = new Consumer[Animal,Bird]()
val c2:Consumer[Bird,Animal] = c
c2.m1(new Animal)
c2.m2(new Bird)
}

View Bound <%

Scala还有一种视图绑定的功能,如

    class Bird {def sing = {}}
class Toy {}
class Consumer[T <% Bird]() {
def use(t: T) = t.sing
}

或者类型参数在方法上:

    class Bird {def sing = {}}
class Toy {}
class Consumer() {
def use[T <% Bird](t: T) = t.sing
}
class Test extends App {
val c = new Consumer()
c.use(new Toy)
}

它要求T必须有一种隐式转换能转换成Bird,也就是 T => Bird,否则上面的代码会编译出错:
No implicit view available from Toy => Bird.
加入一个隐式转换,编译通过。

    import scala.language.implicitConversions
class Bird {def sing = {}}
class Toy {}
class Consumer() {
def use[T <% Bird](t: T) = t.sing
}
class Test extends App {
implicit def toy2Bird(t: Toy) = new Bird
val c = new Consumer()
c.use(new Toy)
}

Context Bound

context bound在Scala 2.8.0中引入,也被称作type class pattern。
view bound使用A <% String方式,context bound则需要参数化的类型,如Ordered[A]。
它声明了一个类型A,隐式地有一个类型B[A],语法如下:

    def f[A : B](a: A) = g(a) // where g requires an implicit value of type B[A]

更清晰的一个例子:

    def f[A : ClassManifest](n: Int) = new Array[A](n)

又比如

    def f[A : Ordering](a: A, b: A) = implicitly[Ordering[A]].compare(a, b)

参考

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

Scala的协变和逆变上界与下界

协变点和逆变点

scala-协变、逆变、上界、下界的更多相关文章

  1. 解读经典《C#高级编程》最全泛型协变逆变解读 页127-131.章4

    前言 本篇继续讲解泛型.上一篇讲解了泛型类的定义细节.本篇继续讲解泛型接口. 泛型接口 使用泛型可定义接口,即在接口中定义的方法可以带泛型参数.然后由继承接口的类实现泛型方法.用法和继承泛型类基本没有 ...

  2. 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 ...

  3. C#的in/out关键字与协变逆变

    C#提供了一组关键字in&out,在泛型接口和泛型委托中,若不使用关键字修饰类型参数T,则该类型参数是不可变的(即不允许协变/逆变转换),若使用in修饰类型参数T,保证"只将T用于输 ...

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

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

  5. java协变逆变,PECS

    public static void main(String[] args) { // Object <- Fruit <- Apple <- RedApple System.out ...

  6. 协变 & 逆变

    都跟里氏替换原则有关. 协变:你可以用一个子类对象去替换相应的一个父类对象,这是完全符合里氏替换原则的,和协(谐)的变.如:用Swan替换Bird. 逆变:你可以用一个父类对象去替换相应的一个子类对象 ...

  7. C#核心语法讲解-泛型(详细讲解泛型方法、泛型类、泛型接口、泛型约束,了解协变逆变)

    泛型(generic)是C#语言2.0和通用语言运行时(CLR)的一个新特性.泛型为.NET框架引入了类型参数(type parameters)的概念.类型参数使得设计类和方法时,不必确定一个或多个具 ...

  8. C#核心语法-泛型(详细讲解泛型方法、泛型类、泛型接口、泛型约束,了解协变逆变)

    泛型(generic)是C#语言2.0和通用语言运行时(CLR)的一个新特性.泛型为.NET框架引入了类型参数(type parameters)的概念.类型参数使得设计类和方法时,不必确定一个或多个具 ...

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

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

随机推荐

  1. SGU 275 To xor or not to xor 高斯消元求N个数中选择任意数XORmax

    275. To xor or not to xor   The sequence of non-negative integers A1, A2, ..., AN is given. You are ...

  2. Intent界面跳转与传递数据

    Activity跳转与传值,主要是通过Intent类,Intent的作用是激活组件和附带数据. intent可以激活Activity,服务,广播三类组件. 本博文讲的是显示意图激活Activity组件 ...

  3. Liferay 6.2 改造系列之二十一:修改WebSphare下JSONWS服务不生效的BUG

    问题原因是WebSphare下,servletContext.getContextPath()获取到的值为“/”而非空字符串. 在/portal-master/portal-impl/src/com/ ...

  4. 分享Kali Linux 2016.2第46周镜像文件

    分享Kali Linux 2016.2第46周镜像文件Kali Linux官网在11月13日发布Kali Linux 2016.2的第46周镜像文件.这次还是保持以往的规模,总共提供了11个镜像文件. ...

  5. VS2013单元测试及代码覆盖率分析--Xunit

    1,Javaweb中有jmeter.jacoco.ant.badboy等集成测试代码覆盖率的方式,C#代码的覆盖率怎么测试呢?VS2013的IDE上本身并未集成测试的工具,以下讲解VS2013中C#代 ...

  6. bzoj1008 [HNOI2008]越狱

    1008: [HNOI2008]越狱 Time Limit: 1 Sec  Memory Limit: 162 MBSubmit: 5099  Solved: 2207 Description 监狱有 ...

  7. 【原】iOS学习43即时通信之XMPP(2)

    本篇是 即时通信之XMPP(2) 接上次 即时通信之XMPP(1) 1. 好友列表 1> 初始化好友花名册 // 获取管理好友的单例对象 XMPPRosterCoreDataStorage *r ...

  8. BZOJ4421 : [Cerc2015] Digit Division

    如果两个相邻的串可行,那么它们合并后一定可行,所以求出所有可行的串的个数$t$,则$ans=2^{t-1}$. 注意特判整个串不可行的情况,这个时候答案为0. #include<cstdio&g ...

  9. 【BZOJ1984】月下“毛景树” 树链剖分+线段树

    [BZOJ1984]月下"毛景树" Description 毛毛虫经过及时的变形,最终逃过的一劫,离开了菜妈的菜园. 毛毛虫经过千山万水,历尽千辛万苦,最后来到了小小的绍兴一中的校 ...

  10. ACM 素数

    素数 时间限制:3000 ms  |  内存限制:65535 KB 难度:1   描述 走进世博园某信息通信馆,参观者将获得前所未有的尖端互动体验,一场充满创想和喜悦的信息通信互动体验秀将以全新形式呈 ...