文档章节

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

jacksu
 jacksu
发布于 2015/11/20 07:46
字数 1177
阅读 2654
收藏 56
点赞 6
评论 5

测试源代码

欢迎大家关注: scala工具库 ,里面包含各种库的测试用例和使用说明文档

协变逆变

B是A的子类,A是B的父类。

当我们定义一个协变类型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

cov不能赋值给cov2,因为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

因为Covariant定义成协变类型的,所以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

这里Contravariant[-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的协变和逆变上界与下界

协变点和逆变点

© 著作权归作者所有

共有 人打赏支持
jacksu
粉丝 7
博文 11
码字总数 6364
作品 1
西安
高级程序员
加载中

评论(5)

askHell
askHell
协变张量、逆变张量是张量分析的概念,用于将同一个“不变量”在不同的“坐标系”之间进行转换。
jacksu
jacksu

引用来自“pseudo”的评论

文章里好多错误,不知道是复制的还是写的时候眼睛花了,写完也不审查下。对自己的博客都这么没有责任心
修改后的内容会更新到github中
jacksu
jacksu

引用来自“pseudo”的评论

文章里好多错误,不知道是复制的还是写的时候眼睛花了,写完也不审查下。对自己的博客都这么没有责任心
会继续努力
__c
__c
scala里居然还有这种概念啊……学过相对论或者微分几何的应该知道逆变张量和协变张量是什么
pseudo
pseudo
文章里好多错误,不知道是复制的还是写的时候眼睛花了,写完也不审查下。对自己的博客都这么没有责任心
Scala的协变covariant(+),逆变contravariant(-),上界(:)

原文:https://my.oschina.net/xinxingegeya/blog/486671 Scala的协变(+),逆变(-),上界(<:),下界(>:) 协变covariant、逆变contravariant、不可变invariant 对于一个带类型参数的类型,比如...

u013063153
2017/11/09
0
0
Scala的协变(+),逆变(-),上界(:)

Scala的协变(+),逆变(-),上界(<:),下界(>:) 协变covariant、逆变contravariant、不可变invariant 对于一个带类型参数的类型,比如 List[T],如果对A及其子类型B,满足 List[B]也符合List[...

秋风醉了
2015/08/02
0
0
Scala之类型参数化:Type Parameterization

Scala之类型参数化:Type Parameterization 本文原文出处: http://blog.csdn.net/bluishglc/article/details/52584401 严禁任何形式的转载,否则将委托CSDN官方维护权益! 型变:Variance 型...

bluishglc
2016/09/19
0
0
Scala版的生产者消费者模型&泛型中的协变(Covariance)与逆变(Contravar...

class BoundedBufferA : ClassManifest {var in = 0var out = 0var n = 0 val elems = new ArrayA def put(x: A) = synchronized {while (n >= N) wait()elems(in) = xin = (in + 1) % Nn = ......

开源中国驻成都办事处
2012/07/03
0
0
Scala 语言学习之泛型(7)

==> 泛型类 ---> T 可以代表任意类型 ==> 泛型函数 ---> 类型参数放在方法名之后 ==> 类型的上界和下界(Upper Bounds 和 Lower Bounds) ---> 用来定义泛型变量的范围 : ---> S <: T (上界......

科技小能手
2017/11/12
0
0
Kotlin 泛型 VS Java 泛型

建议先阅读我的上一篇文章 -- Java 泛型 和 Java 泛型一样,Kotlin 泛型也是 Kotlin 语言中较难理解的一个部分。Kotlin 泛型的本质也是参数化类型,并且提供了编译时强类型检查,实际上也是伪...

JohnnyShieh
06/11
0
0
scala高阶函数

模式匹配  模式匹配  样例类  封闭类  偏函数 it-auto; text-indent: 0px; text-transf模式匹配  模式匹配是数据结构上的概念  val hi=Array("hi","hello","world")  hi match...

泡海椒
2016/04/18
49
0
Java泛型的协变、逆变和不变

背景 平时在看一些开源框架源码时总发现他们会或多或少的用到泛型来定义数据类型。这可以理解,毕竟牛逼的开源框架大都是为了解决一类普遍问题而存在的;但看不懂的是,有时参数或者返回值会...

JarryWell
07/11
0
0
Java中逆变与协变

看下面一段代码 Number num = new Integer(1); ArrayList<Number> list = new ArrayList<Integer>(); //type mismatch List<? extends Number> list = new ArrayList<Number>();list.add(new......

Hosee
2016/02/25
159
0
Scala 高阶函数(high-order function)剖析

Scala 是一种函数式编程语言,也就是说每一个函数都是一个值。Scala 有很简洁的语法用于定义匿名函数、curry 化函数(curried function)、应用函数、偏函数以及嵌套函数等。函数式编程由数学...

Barudisshu
2015/06/30
0
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

Java基础——异常

声明:本栏目所使用的素材都是凯哥学堂VIP学员所写,学员有权匿名,对文章有最终解释权;凯哥学堂旨在促进VIP学员互相学习的基础上公开笔记。 异常处理: 可以挖很多个陷阱,但是不要都是一样...

凯哥学堂
31分钟前
0
0
180723-Quick-Task 动态脚本支持框架之结构设计篇

文章链接:https://liuyueyi.github.io/hexblog/2018/07/23/180723-Quick-Task-动态脚本支持框架之结构设计篇/ Quick-Task 动态脚本支持框架之结构设计篇 相关博文: 180702-QuickTask动态脚本...

小灰灰Blog
34分钟前
0
0
SBT 常用开发技巧

SBT 一直以来都是 Scala 开发者不可言说的痛,最主要的原因就是官方文档维护质量较差,没有经过系统的、循序渐进式的整理,导致初学者入门门槛较高。虽然也有其它构建工具可以选择(例如 Mill...

joymufeng
39分钟前
0
0
HBase in Practice - 性能、监控及问题解决

李钰(社区ID:Yu Li),阿里巴巴计算平台事业部高级技术专家,HBase开源社区PMC&committer。开源技术爱好者,主要关注分布式系统设计、大数据基础平台建设等领域。连续4年基于HBase/HDFS设计和...

中国HBase技术社区
40分钟前
1
0
ES18-JAVA API 批量操作

1.批量查询 Multi Get API public static void multiGet() {// 批量查询MultiGetResponse response = getClient().prepareMultiGet().add("my_person", "my_index", "1")// 查......

贾峰uk
44分钟前
0
0
SpringBoot2.0使用health

1,引入actuator <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-actuator</artifactId></dependency> 2,application.properties ......

暗中观察
51分钟前
0
0
阿里巴巴Java开发规约

###编程规约 命名风格 【强制】代码中的命名均不能以下划线或美元符号开始,也不能以下划线或美元符号结束 【强制】代码中的命名严禁使用拼音与英文混合的方式,更不允许直接使用中文的方式。...

简心
56分钟前
0
0
如何用TypeScript来创建一个简单的Web应用

转载地址 如何用TypeScript来创建一个简单的Web应用 安装TypeScript 获取TypeScript工具的方式: 通过npm(Node.js包管理器) npm install -g typescript 构建你的第一个TypeScript文件 创建...

durban
今天
0
0
分享好友,朋友圈自定义分享链接无效

这个问题是微信6.5.6版本以后,修改了分享规则:分享的连接必须在公众号后台设定的js安全域名内

LM_Mike
今天
0
0
2018年7月23日课程

一、LVS-DR介绍 director分配请求到不同的real server。real server 处理请求后直接回应给用户,这样director负载均衡器仅处理客户机与服务器的一半连接。负载均衡器仅处理一半的连接,避免了...

人在艹木中
今天
0
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部