首先定义一个类 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. PL/SQL 记录 Record 简介

    记录类型是表中当行数据结构的一个镜像.每个记录只存储一行数据,记录包含的是字段,而不是列. 1.用%rowtype属性隐式定义记录类型 declare individual individuals%r ...

  2. java.io.FileNotFoundException: /opt/apache-tomcat-7.0.57/conf/server.xml (权限不够)

    1 错误描述 youhaidong@youhaidong:~$ cd /opt/apache-tomcat-7.0.57 youhaidong@youhaidong:/opt/apache-tomca ...

  3. E: 未发现软件包 install_flash_player_11_linux.x86_64.tar.gz

    1 错误描述 youhaidong@youhaidong:~$ sudo apt-get install install_flash_player_11_linux.x86_64.tar.gz 正在读 ...

  4. Keras官方中文文档:序贯模型API

    Sequential模型接口 如果刚开始学习Sequential模型,请首先移步这里阅读文档,本节内容是Sequential的API和参数介绍. 常用Sequential属性 model.layers ...

  5. 【BZOJ1415】【NOI2005】聪聪和可可(动态规划,数学期望)

    [BZOJ1415][NOI2005]聪聪和可可(动态规划,数学期望) 题面 BZOJ 题解 先预处理出当可可在某个点,聪聪在某个点时 聪聪会往哪里走 然后记忆化搜索一下就好了 #include< ...

  6. [Luogu4074][WC2013]糖果公园

    BZOJ权限题!提供洛谷链接 sol 树上带修改莫队 很显然吧.对吧. 所以说树上莫队要怎么写呢? 我们知道莫队=给区间排序+依次暴力处理,所以对于树上莫队而言也是一样的. 序列莫队基于序列分块(也就 ...

  7. Luogu3242:[HNOI2015]接水果

    题面 Luogu3242 Sol 考虑每个盘子怎样才能接到一个水果 分两种情况: 盘子的\(x, y\)在一条链上,那么水果的两点就要在这条链之外 不在的话,水果的两点就分别在盘子的两点的子树中 记录 ...

  8. Java interview questions(No1)

    1.什么是构造和析构方法?功能是? 答: 构造方法: 每个类至少有一个构造方法,类初始化时调用的方法 1.方法名和类名相同 2.无返回值类型 格式:访问权限 类名(参数列表) {}; 1.自己定义构造 ...

  9. php数组基础知识

    php数组 <?php $kele = array('张三',10 => '李四', 'PHP中文网' , '去PHP中文网学PHP', 19 => '王二' , '小明'); // ...

  10. VMware Workstation 14 Pro永久激活密钥

    1. ZC3WK-AFXEK-488JP-A7MQX-XL8YF 2. AC5XK-0ZD4H-088HP-9NQZV-ZG2R4 3. ZY5H0-D3Y8K-M89EZ-AYPEG-MYUA8 4 ...