Scala中协变(+)、逆变(-)、上界(:)、下界(:)简单介绍
对于一个带类型参数的类型,比如 List[T],如果对A及其子类型B,满足 List[B]也符合List[A]的子类型,那么就称为covariance(协变) ,
如果 List[A]是 List[B]的子类型,即与原来的父子关系正相反,则称为contravariance(逆变)。
如果一个类型支持协变或逆变,则称这个类型为variance(翻译为可变的或变型),否则称为invariance(不可变的)
而scala支持,可以在定义类型时声明(用加号表示为协变,减号表示逆变),如:
tarit Person[+T]{} --协变,在这种情况下,假设S类型是A类型的子类,则Person[S]类型可泛化成Person[T]的子类,也就是被参数化类型的泛化方向与参数类型的方向是一致的,所以称为协变。
tarit Person[-T]{} --逆变,这种情况下,当类型S是类型A的子类型,则Person[A]反过来可以认为是Person[S]的子类型。也就是被参数化类型的泛化方向与参数类型的方向是相反的,所以称为逆变
Scala的协变和逆变
class Super
class Sub extends Super
class Temp[+A](title: String)//支持协变
object app_main extends App {
val t: Temp[Super] = new Temp[Sub]("hello world")
print(t.toString)
}
但要注意的是,variance(可变类型)并不会被继承,父类声明为variance(可变类型),子类如果想要保持,仍需要声明成可变类型,如下代码:
scala> trait A[+T]
defined trait A
scala> class C[T] extends A[T]
defined class C
scala> class X; class Y extends X;
defined class X
defined class Y
scala> val c:C[X] = new C[Y] //不会继承父类的可变类型
<console>:11: error: type mismatch;
found : C[Y]
required: C[X]
Note: Y <: X, but class C is invariant in type T.
You may wish to define T as +T instead. (SLS 4.5)
val c:C[X] = new C[Y]
代码修改如下
scala> class C[+T] extends A[T]
defined class C
scala> val c:C[X] = new C[Y]
c: C[X] = C@7241a47d
Scala逆变和协变的实例分析
在Scala(以及其他许多编程语言)中,函数也是对象,可以使用、定义其他对象的地方,也可以使用、定义函数。Scala中的函数,具有apply方法的类的实例,就可以当做函数来使用。其中apply接受的参数就是函数的参数,而apply的返回值就是函数的返回值。
首先给出一个接受一个参数的函数的泛型定义。
trait Function1[-T, +U] {
def apply(x: T): U
}
这种函数接受一个参数,参数类型为泛型类型T,返回类型为泛型类型U。和其他支持泛型的语言一样,实际定义函数时T和U的类型会被确定下来,不过需要注意的是,这边的T之前有一个“-”,而U之前有一个“+”。
在这里引入关于这个符号的说明,在声明Scala的泛型类型时,“+”表示协变,而“-”表示逆变
C[+T]:如果A是B的子类,那么C[A]是C[B]的子类。>>>>>协变
C[-T]:如果A是B的子类,那么C[B]是C[A]的子类。>>>>>逆变
C[T]:无论A和B是什么关系,C[A]和C[B]没有从属关系。
根据Liskov替换原则, 如果A是B的子类,那么能适用于B的所有操作,都适用于A。 让我们看看这边Function1的定义,是否满足这样的条件。假设Bird是Animal的子类,那么看看下面两个函数之间是什么关系:
def f1(x: Bird): Animal // instance of Function1[Bird, Animal]
def f2(x: Animal): Bird // instance of Function1[Animal, Bird]
在这里f2的类型是f1的类型的子类。为什么?
我们先看一下参数类型,根据Liskov替换原则,f1能够接受的参数,f2也能接受 。在这里f1接受的Bird类型,f2显然可以接受,因为Bird对象可以被当做其父类Animal的对象来使用。
再看返回类型,f1的返回值可以被当做Animal的实例使用,f2的返回值可以被当做Bird的实例使用,当然也可以被当做Animal的实例使用。
所以我们说,函数的参数类型是逆变的,而函数的返回类型是协变的。
函数的参数类型是逆变的,而函数的返回类型是协变的
那么我们在定义Scala类的时候,是不是可以随便指定泛型类型为协变或者逆变呢?答案是否定的。通过上面的例子可以看出,如果将Function1的参数类型定义为协变,或者返回类型定义为逆变,都会违反Liskov替换原则,因此,Scala规定,协变类型只能作为方法的返回类型,而逆变类型只能作为方法的参数类型。类比函数的行为,结合Liskov替换原则,就能发现这样的规定是非常合理的。
Scala上界(<:)和下界(>:)
1) U >: T
这是类型下界的定义,也就是U必须是类型T的父类(或本身,自己也可以认为是自己的父类)。
2) S <: T
这是类型上界的定义,也就是S必须是类型T的子类(或本身,自己也可以认为是自己的子类)。
参考博客地址:http://my.oschina.net/xinxingegeya/blog/486671
Scala中协变(+)、逆变(-)、上界(:)、下界(:)简单介绍相关推荐
- C#泛谈 —— 变体(协变/逆变)
有如下四个类. public class Animal{}public class Mammal : Animal{}public class Dog : Mammal{public void Eat ...
- 12:设计模式、泛型、上下界、视图界定、上下文界定、协变逆变不变
经典的 WordCount 的讲解 示例代码如下: package com.atguigu.chapter14.homework.wordcount/*val lines = List("a ...
- 大数据技术之_16_Scala学习_12_设计模式+泛型、上下界、视图界定、上下文界定、协变逆变不变
大数据技术之_16_Scala学习_12 第十七章 设计模式 17.1 学习设计模式的必要性 17.2 掌握设计模式的层次 17.3 设计模式的介绍 17.4 设计模式的类型 17.5 简单工厂模式( ...
- 跟着小老弟来学习Kotlin中的逆变和协变
/ 今日科技快讯 / 近日,小米创始人.董事长兼CEO雷军在抖音上开启了其直播带货的首秀.从晚上8点开播,到晚上10点,销售额就已经破亿.包括1000台售价49999元的透明电视在内的商品一推 ...
- java中的逆变、协变、不变概念讲解转载自http://www.cnblogs.com/en-heng/p/5041124.html,感谢编程路上的前辈们!
En-Heng 无他,但手熟尔 博客园 首页 新随笔 联系 订阅 管理 随笔 - 32 文章 - 0 评论 - 33 Java中的逆变与协变 看下面一段代码 Number num = new In ...
- 协变逆变java_Java中的逆变与协变
什么是逆变与协变 协变(Covariance) 如果B是A的子类,并且F(B)也是F(A)的子类,那么F即为协变 逆变(Contravariance) 如果B是A的子类,并且F(B)成了F(A)的父类 ...
- 协变逆变java_Java中的协变与逆变
Java作为面向对象的典型语言,相比于C++而言,对类的继承和派生有着更简洁的设计(比如单根继承). 在继承派生的过程中,是符合Liskov替换原则(LSP)的.LSP总结起来,就一句话: 所有引用基 ...
- 泛型型协变逆变_Java泛型类型简介:协变和逆变
泛型型协变逆变 by Fabian Terh 由Fabian Terh Java泛型类型简介:协变和逆变 (An introduction to generic types in Java: cova ...
- 10天学会kotlin DAY7 接口 泛型 协变 逆变
kotlin 接口 泛型 协变 逆变 前言 1.接口的定义 2.抽象类 3.定义泛型类 4.泛型函数 5.泛型变换 6.泛型类型约束 7.vararg 关键字(动态参数) 8.[] 操作符 9.out ...
- UPS电源中的逆变电路与Simulink仿真
一.设计题目: UPS电源中的逆变电路 主要指标及要求: 输入电压240V 输出电压220/230/240V 效率>90% 二.设计思路.方案选择及电路工作原理: 采用单相全桥逆变电路,并使用双 ...
最新文章
- 记一次安装多版本php的四个雷区,你踩着了吗
- 3项目里面全局用less变量 cli vue_vue-cli3 如何全局引入less变量
- UNITY2018 真机开启deepprofiling的操作
- OpenCASCADE绘制测试线束:布尔运算命令之两个操作数的布尔运算
- Android中的popupwindow从底部进入和退出的动画效果
- MVC系列博客之排球计分(六)Controller的实现(二)
- [方便WAP网站开发]在线手机WAP模拟器或软件
- 算法界的“视界杯”,2021腾讯广告算法大赛来了!
- An动画基础之元件的影片剪辑动画与传统补间
- luogu1600天天爱跑步
- JSON数据中带有HTML标签解决方法
- 关于门户网站下载文件.doc文件直接打开成压缩包格式的解决办法
- 【Ubuntu】Ubuntu18.04无法识别移动硬盘、U盘解决方法
- 微信浏览器(jssdk)自定义分享按钮,自定义链接,图片,描述等
- 管理经济学 知识点总结(一)
- JDBC Mybatis 调用 ORACLE 存储过程 函数 返回 varray 类型 function return varray out varray
- 《C语言程序设计》江宝钏主编-习题3-6-商和余
- 正确重启计算机的方法,电脑一开机就会出现 重启并选择正确的启动设备或在选定的启动设...
- 浅谈:电商系统常见的几种模式
- 一方包、二方包、三方包是什么?
热门文章
- css3 动画加旋转 例子
- IOS微信逆向-免越狱抢红包防撤回等自定义功能实现
- The GridView 'gv ' fired event RowEditing which wasn 't handled.
- 第一行代码-第二版(郭霖著)笔记四(Fragment)
- H5 div移动效果
- vue-router4.x 路由配置(多种方案)
- 全景照片做成html,全景嵌入HTML页面插件
- 拍摄360全景照片多少钱?全景图片用什么软件看?
- 微信小程序如何登陆管理后台并且绑定开发者账号?
- http返回码是000...