首先定义一个类 A,其参数类型 T 为协变,类中包含一个方法 func,该方法有一个类型为 T 的参数:

 class A[+T] {
def func(x: T) {}
}

此时在 x 处会有异常提示,covariant type T occurs in contravariant position in type T of value x

现在,先假设该类定义编译可以通过;
因为 String→AnyRef(String 是 AnyRef 的子类),参数类型 T 为协变,所以 A[String]→A[AnyRef](A[String] 也是 A[AnyRef] 的子类)。
定义两个对象如下:
val father: A[AnyRef] = null.asInstanceOf[A[AnyRef]] // 父类对象,包含方法 func(x: AnyRef)
val child: A[String] = null.asInstanceOf[A[String]] // 子类对象,包含方法 func(x: String)
根据里氏替换原则,调用 father.func(Nil) 里的 father 对象应该可以直接替换成 child 对象,但是 child.func(Nil) 调用显然异常。
或者定义如下一个函数,接收 A[AnyRef] 作为参数:
 def otherFunc(x: A[AnyRef]): Unit = {
x.func(Nil)
}
同理调用 otherFunc(father) 里的 father 对象也应该可以直接替换成 child 对象,但是如此一来 otherFunc 中的参数要调用 func 方法就只能传入 String 类型的参数了(因为 child 对象中的 func 方法只能接收 String 类型参数),相当于 otherFunc 的处理范围缩小了,这是不允许的。
也就是说上面 A 类的定义违反了里氏替换原则,所以编译器无法通过。
图解上述分析过程:
反之,如果用 father 的父类对象替换,相当于 otherFunc 的处理范围变大了,此时 otherFunc 中的参数要调用 func 方法,完成可以传入 AnyRef 类型的参数。
 val fatherFather: A[Any] = null.asInstanceOf[A[Any]] // func(x: Any)
otherFunc(fatherFather)// 如果 T 为协变,此处会提示异常

总结:

协变点(covariant position):方法返回值的位置;
逆变点(contravariant position):方法参数的位置;
因为 A 中的类型参数 T 声明为协变,而 T 又是 func 方法中的参数类型,属于逆变点,所以此时编译器会提示异常:
covariant type T occurs in contravariant position in type T of value x
 
 
  • 如果将 T 作为 func 方法的返回值类型,即处于协变点,就可以编译通过。
        此时,回到之前的 otherFunc(father) 和 otherFunc(child),child 替换 father 后,child 调用 func 返回 String 类型的对象替换 AnyRef 是合理的。
 class B[+T] {
def func(): T = {
null.asInstanceOf[T]
}
}
// 与 Function0[+A] 等价
  • 或者将类型参数 T 改为逆变,
 class A[-T] {
def func(x: T) {}
}
// 与 Function1[-A, Unit] 等价 val father: A[AnyRef] = null.asInstanceOf[A[AnyRef]] // func(x: AnyRef)
val fatherFather: A[Any] = null.asInstanceOf[A[Any]] // func(x: Any) def otherFunc(x: A[AnyRef]): Unit = {
x.func(Nil)
} otherFunc(father) // success
otherFunc(fatherFather)// success
 

参考网址:

 
by. Memento

span::selection, .codemirror-line > span > span::selection { background: #d7d4f0; }.codemirror-line::-moz-selection, .codemirror-line > span::-moz-selection, .codemirror-line > span > span::-moz-selection { background: #d7d4f0; }.cm-searching {background: #ffa; background: rgba(255, 255, 0, .4);}.cm-force-border { padding-right: .1px; }@media print { .codemirror div.codemirror-cursors {visibility: hidden;}}.cm-tab-wrap-hack:after { content: ""; }span.codemirror-selectedtext { background: none; }.codemirror-activeline-background, .codemirror-selected {transition: visibility 0ms 100ms;}.codemirror-blur .codemirror-activeline-background, .codemirror-blur .codemirror-selected {visibility:hidden;}.codemirror-blur .codemirror-matchingbracket {color:inherit !important;outline:none !important;text-decoration:none !important;}
-->
li {list-style-type:decimal;}.wiz-editor-body ol.wiz-list-level2 > li {list-style-type:lower-latin;}.wiz-editor-body ol.wiz-list-level3 > li {list-style-type:lower-roman;}.wiz-editor-body blockquote {padding:0 12px;padding:0 0.75rem;}.wiz-editor-body blockquote > :first-child {margin-top:0;}.wiz-editor-body blockquote > :last-child {margin-bottom:0;}.wiz-editor-body img {border:0;max-width:100%;height:auto !important;margin:2px 0;}.wiz-editor-body table {border-collapse:collapse;border:1px solid #bbbbbb;}.wiz-editor-body td,.wiz-editor-body th {padding:4px 8px;border-collapse:collapse;border:1px solid #bbbbbb;min-height:28px;word-break:break-all;box-sizing: border-box;}.wiz-hide {display:none !important;}
-->

[Scala] 了解 协变 与 逆变的更多相关文章

  1. (转)Scala中协变(+)、逆变(-)、上界(<:)、下界(>:)简单介绍

    看源码的时候看到: trait ExtensionId[T <: Extension] { 没见过这个符号啊<: Scala上界(<:)和下界(>:) 1) U >: T ...

  2. scala学习笔记-类型参数中协变(+)、逆变(-)、类型上界(<:)和类型下界(>:)的使用

    转载自  fineqtbull   http://fineqtbull.iteye.com/blog/477994 有位je上的同学来短信向我问起了Scala类型参数中协变.逆变.类型上界和类型下界的 ...

  3. Scala类型参数中协变(+)、逆变(-)、类型上界(<:)和类型下界(>:)的使用

    转自:http://fineqtbull.iteye.com/blog/477994#bc2364938 有位je上的同学来短信向我问起了Scala类型参数中协变.逆变.类型上界和类型下界的使用方法和 ...

  4. Scala 基础(十六):泛型、类型约束-上界(Upper Bounds)/下界(lower bounds)、视图界定(View bounds)、上下文界定(Context bounds)、协变、逆变和不变

    1 泛型 1)如果我们要求函数的参数可以接受任意类型.可以使用泛型,这个类型可以代表任意的数据类型. 2)例如 List,在创建 List 时,可以传入整型.字符串.浮点数等等任意类型.那是因为 Li ...

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

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

  6. Scala教程之:深入理解协变和逆变

    文章目录 函数的参数和返回值 可变类型的变异 在之前的文章中我们简单的介绍过scala中的协变和逆变,我们使用+ 来表示协变类型:使用-表示逆变类型:非转化类型不需要添加标记. 假如我们定义一个cla ...

  7. 不变(Invariant), 协变(Covarinat), 逆变(Contravariant) : 一个程序猿进化的故事

    阿袁工作的第1天: 不变(Invariant), 协变(Covarinat), 逆变(Contravariant)的初次约 阿袁,早!开始工作吧. 阿袁在笔记上写下今天工作清单: 实现一个scala类 ...

  8. 从底层实现剖析Kotlin协变与逆变的原理

    继续还是探究协变与逆变,在正式开始之前,先来对Kotlin和Java的协变与逆变进行一个对比: 1.Kotlin是声明处协变:而在Java中是在使用处协变: 如何理解,我们先来回顾一下在Java使用协 ...

  9. C#4.0泛型的协变,逆变深入剖析

    C#4.0中有一个新特性:协变与逆变.可能很多人在开发过程中不常用到,但是深入的了解他们,肯定是有好处的. 协变和逆变体现在泛型的接口和委托上面,也就是对泛型参数的声明,可以声明为协变,或者逆变.什么 ...

随机推荐

  1. 手机浏览网页或打开App时莫名弹出支付宝领红包界面的原因及应对措施

    自从支付宝推出扫码领红包活动后,这种模式独特的赏金机制,短时间内吸引了大量的关注,但是随之也产生了很多的问题,比由于如在赏金的驱动下,微信群里铺天盖地的红包口令,朋友圈里各式各样的领红包二维码图片, ...

  2. 12-7jquery选择器学习

    p:odd 选择奇数个数的p标签   p:even选择奇数个数的p标签   p:gt(n)选择下标 大于n的p标签   p:lt(n)选择下标小于n的p标签   $(":root " ...

  3. 【BZOJ1477】青蛙的约会(拓展欧几里得)

    [BZOJ1477]青蛙的约会(拓展欧几里得) 题面 题目描述 两只青蛙在网上相识了,它们聊得很开心,于是觉得很有必要见一面.它们很高兴地发现它们住在同一条纬度线上,于是它们约定各自朝西跳,直到碰面为 ...

  4. [BZOJ1707] [Usaco2007 Nov] tanning分配防晒霜 (贪心)

    Description 奶牛们计划着去海滩上享受日光浴.为了避免皮肤被阳光灼伤,所有C(1 <= C <= 2500)头奶牛必须在出门之前在身上抹防晒霜.第i头奶牛适合的最小和最 大的SP ...

  5. 开发中使用mongoTemplate进行Aggregation聚合查询

    笔记:使用mongo聚合查询(一开始根本没接触过mongo,一点一点慢慢的查资料完成了工作需求) 需求:在订单表中,根据buyerNick分组,统计每个buyerNick的电话.地址.支付总金额以及总 ...

  6. UWP:记录一下这几天踩到的坑

    最近在玩微软的Desktop Bridge项目,遇到了如下几个坑: 1.文档中给的是js项目魔改的方法,其实C#项目也可以魔改加入UWP部分的,区别在于: 不用在项目文件里写<AppxGener ...

  7. 如何使用Git以及GitHub

    Git在程序的版本控制上有着极大的优势,下面是简单对其的简介 Git 的特点: 1 Snapshots, Not Differences 直接记录快照而非差异对比. 传统的版本控制系统(version ...

  8. Unity3D 心跳检测

    在B/S结构的项目开发的过程当中 在服务端与客户端正常的通信之外 服务端通常还需要知道客户端是否还处于连接状态 或者客户端也需要知道服务端是否还处在开启状态 大白话说完了,听一下比较正统的解释吧(摘自 ...

  9. PHP实现发送模板消息到微信公众号

    简述:在这里会具体讲述到如何实现:如何通过后台的代码来实现发送模板消息到已经关注了"心想"公众号的用户. (本人新手,目前实习中,我的所有文档都是在自己开发过程中的记录,有些言语跟 ...

  10. Online Judge(OJ)搭建——5、配置

    Spring 配置一些本地类,还有 HTML form 提交文件的解析器. package per.piers.onlineJudge.config; import org.springframewo ...