首先定义一个类 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. java.text.ParseException: Unparseable date: "2015-06-09 hh:56:19"

    1.错误描述 [DEBUG:]2015-06-09 16:56:19,520 [-------------------transcation start!--------------] java.te ...

  2. Angular21 动态绑定CSS样式

    1 需求 在前端开发中通常需要动态对某些元素的样式进行动态设定,传统的CSS绑定分为CSS类绑定和Style样式绑定:在Angular中利用相关指令同样可以完成CSS类绑定和CSS样式绑定 2 内置指 ...

  3. Blending, Bootstrap

    听林轩田老师的<机器学习技法>,Lecture 7讲到model的blending. 理解了一个之前一直模棱两可的概念:bootstrap. 先说一下什么是blending.在机器学习中, ...

  4. WebView的使用--Hybrid App

    App页面是运行在WebView中的,一个App页面对应一个WebView,本例实现两个WebView之间的跳转. 实现过程(用到了MUI框架): 1.页面标识+跳转按钮(index.html.mai ...

  5. mysql中的union用法以及子查询综合应用

    union查询就是把2条或者多条sql语句的查询结果,合并成一个结果集. 如:sql1: N行,sql2: M行,sql1 union sql2 ---> N+M行 1.能否从2张表查询再uni ...

  6. as版本切换导致项目损坏,无法启动。环境依赖混乱

    以下方法都不管用, 新建一个项目setting的 把main拷过去就行了!!!! Error:(5, 0) Gradle DSL method not found: 'google()'Possibl ...

  7. 【BZOJ4827】【HNOI2017】礼物(FFT)

    [BZOJ4827][HNOI2017]礼物(FFT) 题面 Description 我的室友最近喜欢上了一个可爱的小女生.马上就要到她的生日了,他决定买一对情侣手 环,一个留给自己,一 个送给她.每 ...

  8. POJ1743:Musical Theme

    题目 vjudge Sol 先差分 然后求不可重叠最长重复子串 bits/stdc++.h会CE # include <bits/stdc++.h> # define IL inline ...

  9. 打造MacOS版“XShell”

    1.背景 XShell作为一个强大的安全终端模拟软件,它支持SSH1, SSH2, 以及Microsoft Windows 平台的TELNET 协议.作为server端开发,几乎是必备工具了. 很多刚 ...

  10. php 目录处理函数

    之前我们处理的全都是文件,那目录和文件夹怎么处理呢? 我们就来学习目录或者称为文件夹的处理相关函数. 处理文件夹的基本思想如下: 1.读取某个路径的时候判断是否是文件夹 2.是文件夹的话,打开指定文件 ...