测试源代码

欢迎大家关注: 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] = cc2.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.implicitConversionsclass 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 Birdval 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的协变和逆变上界与下界

协变点和逆变点

转载于:https://www.cnblogs.com/jacksu-tencent/p/4979666.html

scala-协变、逆变、上界、下界相关推荐

  1. 12:设计模式、泛型、上下界、视图界定、上下文界定、协变逆变不变

    经典的 WordCount 的讲解 示例代码如下: package com.atguigu.chapter14.homework.wordcount/*val lines = List("a ...

  2. 大数据技术之_16_Scala学习_12_设计模式+泛型、上下界、视图界定、上下文界定、协变逆变不变

    大数据技术之_16_Scala学习_12 第十七章 设计模式 17.1 学习设计模式的必要性 17.2 掌握设计模式的层次 17.3 设计模式的介绍 17.4 设计模式的类型 17.5 简单工厂模式( ...

  3. 协变逆变java_Java中的逆变与协变

    什么是逆变与协变 协变(Covariance) 如果B是A的子类,并且F(B)也是F(A)的子类,那么F即为协变 逆变(Contravariance) 如果B是A的子类,并且F(B)成了F(A)的父类 ...

  4. 泛型型协变逆变_Java泛型类型简介:协变和逆变

    泛型型协变逆变 by Fabian Terh 由Fabian Terh Java泛型类型简介:协变和逆变 (An introduction to generic types in Java: cova ...

  5. Scala之“逆变”合理性的思考

    Scala之"逆变"合理性的思考 对于逆变的概念可以参考本系列的前一篇文章: Scala之类型参数化:Type Parameterization 本文的重点是要解释"逆变 ...

  6. C#泛谈 —— 变体(协变/逆变)

    有如下四个类. public class Animal{}public class Mammal : Animal{}public class Dog : Mammal{public void Eat ...

  7. 协变逆变java_Java中的协变与逆变

    Java作为面向对象的典型语言,相比于C++而言,对类的继承和派生有着更简洁的设计(比如单根继承). 在继承派生的过程中,是符合Liskov替换原则(LSP)的.LSP总结起来,就一句话: 所有引用基 ...

  8. 10天学会kotlin DAY7 接口 泛型 协变 逆变

    kotlin 接口 泛型 协变 逆变 前言 1.接口的定义 2.抽象类 3.定义泛型类 4.泛型函数 5.泛型变换 6.泛型类型约束 7.vararg 关键字(动态参数) 8.[] 操作符 9.out ...

  9. Scala语言学习笔记——泛型、上下界、视图界定、上下文界定、协变逆变不变、闭包、柯里化

    1.Scala泛型 应用案例1 /*** @author huleikai* @create 2019-05-27 11:23*/ object TestFanXing {def main(args: ...

  10. 7.scala初识 柯里化、隐式参数、隐式转换、视图边界、上界、下界、协变、逆变

    1.前言: 学过java我们都知道,java中的继承是对类的增强,java中的代理.装饰是对对象方法的增强.而在scala中,隐式转换和隐式参数是Scala中两个非常强大的功能,隐式的对类的方法进行增 ...

最新文章

  1. matlab画CDF曲线
  2. 我拍了拍 Redis,没想到被移出了群聊......
  3. 华为云交付项目服务器配置表,云端服务器配置表
  4. 9.JAVA之GUI编程列出指定目录内容
  5. Oracle超出最大连接数问题及解决
  6. Golden Master Pattern :一种在.NET Core中重构遗留代码的利器
  7. http请求post,返回excel文件,并接收
  8. vue获取上传进度_vue通过input选取apk文件上传,显示进度条
  9. 电话拨号器java_Android基础--电话拨号器
  10. PAT1057. 数零壹
  11. 网卡82546驱动linux,Dell服务器常见Linux驱动选择
  12. Origin: Piper diagram/Trilinear diagram (三线图)
  13. 时空大数据面临的挑战与机遇
  14. 具有深度沉浸能力的人更能有所成就
  15. 首个搭载8MP摄像头的单SoC行泊一体方案来袭,已拿下多家车企定点
  16. systemverilog中rand机制的 $urandom_range()函数
  17. 4g通信模块怎么连接sim卡_4G模块|合宙重磅推出虚拟SIM卡技术,告别卡座
  18. 兼容IE8的旋转角度
  19. FreeSWITCH之配置G729转码
  20. 日常开发中的一些js处理数据的方法,包括对数据的过滤,以及对数据的验证

热门文章

  1. C++ set 多级排序 多维度排序
  2. 最好的git命令行基础使用教程 windows
  3. 关于序列化的几个注意点
  4. matlab axes坐标轴长度,[转载]Matlab 坐标轴(axes),数据提示(data
  5. 10个python数据可视化库_这10个python数据可视化库,通吃任何领域
  6. LeetCode之翻转二叉树以匹配先序遍历
  7. java中max的意义_[Java] xms xmx XX:PermSize XX:MaxPermSize 参数意义解析
  8. 熟练的运用计算机英语怎么说,对什么运用的熟练用英语怎么说?
  9. 双指针-维护不变区域
  10. 拉取 google.golang.org/grpc 报错