对于一个带类型参数的类型,比如 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的泛型类型时,“+”表示协变,而“-”表示逆变

  1. C[+T]:如果A是B的子类,那么C[A]是C[B]的子类。>>>>>协变

  2. C[-T]:如果A是B的子类,那么C[B]是C[A]的子类。>>>>>逆变

  3. 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中协变(+)、逆变(-)、上界(:)、下界(:)简单介绍相关推荐

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

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

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

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

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

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

  4. 跟着小老弟来学习Kotlin中的逆变和协变

    /   今日科技快讯   / 近日,小米创始人.董事长兼CEO雷军在抖音上开启了其直播带货的首秀.从晚上8点开播,到晚上10点,销售额就已经破亿.包括1000台售价49999元的透明电视在内的商品一推 ...

  5. java中的逆变、协变、不变概念讲解转载自http://www.cnblogs.com/en-heng/p/5041124.html,感谢编程路上的前辈们!

    En-Heng 无他,但手熟尔 博客园 首页 新随笔 联系 订阅 管理 随笔 - 32  文章 - 0  评论 - 33 Java中的逆变与协变 看下面一段代码 Number num = new In ...

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

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

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

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

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

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

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

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

  10. UPS电源中的逆变电路与Simulink仿真

    一.设计题目: UPS电源中的逆变电路 主要指标及要求: 输入电压240V 输出电压220/230/240V 效率>90% 二.设计思路.方案选择及电路工作原理: 采用单相全桥逆变电路,并使用双 ...

最新文章

  1. 记一次安装多版本php的四个雷区,你踩着了吗
  2. 3项目里面全局用less变量 cli vue_vue-cli3 如何全局引入less变量
  3. UNITY2018 真机开启deepprofiling的操作
  4. OpenCASCADE绘制测试线束:布尔运算命令之两个操作数的布尔运算
  5. Android中的popupwindow从底部进入和退出的动画效果
  6. MVC系列博客之排球计分(六)Controller的实现(二)
  7. [方便WAP网站开发]在线手机WAP模拟器或软件
  8. 算法界的“视界杯”,2021腾讯广告算法大赛来了!
  9. An动画基础之元件的影片剪辑动画与传统补间
  10. luogu1600天天爱跑步
  11. JSON数据中带有HTML标签解决方法
  12. 关于门户网站下载文件.doc文件直接打开成压缩包格式的解决办法
  13. 【Ubuntu】Ubuntu18.04无法识别移动硬盘、U盘解决方法
  14. 微信浏览器(jssdk)自定义分享按钮,自定义链接,图片,描述等
  15. 管理经济学 知识点总结(一)
  16. JDBC Mybatis 调用 ORACLE 存储过程 函数 返回 varray 类型 function return varray out varray
  17. 《C语言程序设计》江宝钏主编-习题3-6-商和余
  18. 正确重启计算机的方法,电脑一开机就会出现 重启并选择正确的启动设备或在选定的启动设...
  19. 浅谈:电商系统常见的几种模式
  20. 一方包、二方包、三方包是什么?

热门文章

  1. css3 动画加旋转 例子
  2. IOS微信逆向-免越狱抢红包防撤回等自定义功能实现
  3. The GridView 'gv ' fired event RowEditing which wasn 't handled.
  4. 第一行代码-第二版(郭霖著)笔记四(Fragment)
  5. H5 div移动效果
  6. vue-router4.x 路由配置(多种方案)
  7. 全景照片做成html,全景嵌入HTML页面插件
  8. 拍摄360全景照片多少钱?全景图片用什么软件看?
  9. 微信小程序如何登陆管理后台并且绑定开发者账号?
  10. http返回码是000...