Java 泛型 协变性、逆变性

@author ixenos

摘要:协变性、协变通配符、协变数组、协变返回值

协变性、逆变性和无关性


在面向对象的计算机程序语言中,经常涉及到类型之间的转换,例如从具体类小猫到动物之间的类型转换(上行转换),或者从形状向三角形之间的转换(下行转换)。

协变性(covariance)、逆变性(contravariance)和无关性(invariant)。他们都是用来描述类型转换的性质的术语,形式化的描述如下:

如果A和B是类型,f表示类型转换,≤表示子类型关系,(例如A≤B,表示A是B的子类)那么:

如果A≤B 则f(A) ≤ f(B),小推大,那么f是协变的;如果A≤B 则f(B) ≤ f(A),大推小,那么f是逆变的;如果上面两种都不成立,那么f是无关的。

Java语言有一些语法可以用协变性和逆变性来理解


  • Java泛型
class DataHolder<T>{

}

    假如类型 A ≤ B, 但是直接使用DataHolder<A> 和DataHolder<B>是不可协变的,之前我们已经叙述过。

    但是利用Java提供的通配符语法,却可以提供一个协变的类型转换
       DataHolder<A> ≤DataHolder<? Extends B>  // ?是通配符,但这样协变是类型不安全的

    例如:

static boolean find(Iterable<? extends Object> where, Object what){ //通配符?赋给泛型变量,可以有一个协变的类型转换
return false;
}

    协变的类型转换:可使用Iterable<String> 来调用find函数。

协变数组


  协变数组是有漏洞的,具体分析请看:数组协变带来的静态类型漏洞 rednaxelafx.iteye.com/blog/379703

  注意:
例如 A =
String, B = Object 我们这里有A≤B
f(A) = String[], f(B) = Object[]. 因为Object[] objects =
new String[n].(赋值) 所以我们可以认为数组具有协变性

协变返回值(在子类重写方法的时候)


在面向对象语言中,一个协变返回值方法是一个在子类覆盖(Override)该方法的时候,方法的返回值可以被一个“更窄”的类型所替代。(Java JDK5.0)
例如:

public static class Super {
Object getSomething() {
return null;
}
} public static class SubClass extends Super {
@Override
String getSomething() {
return null;
}
}

这段话主要的思想就是协同返回值使用在子类需要一个不同于父类覆盖方法的返回值,但是返回值却有继承关系的情况下。
一个可能更好的例子如下所示:

class Collection {
Iterator iterator() { ... }
} class List extends Collection {
@Override
ListIterator iterator() { ... }
}

Iterator函数获得到当前集合的迭代器,在子类中,迭代器有着更确切的表示,所以使用了Iterator的子类ListIterator作为新的返回值。

逆变参数(形参)


  继承的一个法则:子类不能比父类有更严格的限制
先举个例子,可以一下子明白逆变参数的例子,其实逆变参数使用环境依然是在继承体系下的函数覆盖

class Super {
void doSomething(String parameter) {
}
} class Sub extends Super {
void doSomething(Object parameter) { //逆变了
}
}

为了更好的理解这个问题,首先引入Liskov Substitution Principle的概念。这个原则的内容就是说,子类的对象可以在任何需要父类对象的地方使用
这个原则和参数以及返回值有什么关系呢?
假如我们定义类如下:

class Super {
ReturnType doSomething(ParameterType a) {
return null;
}
} class Sub extends Super {
ReturnType doSomething(ParameterType a) {
return null;
}
}

假如我有Super sup;
首先考虑参数,现在要调用sup.doSomething(o);
如果两个函数需要的参数类型一致,没有什么值得讨论的。假如在一些情况下,sub需要的参数类型变成了和ParameterType有继承关系的另一个类NewParameterType。如下:

class Sub extends Super {
ReturnType doSomething(NewParameterType a) {
return null;
}
}

如果NewParameterType是ParameterType的子类,那么sup.doSomething(o)调用中,如果o是指向NewParameterType的对象,则不会被通过。只有NewParameterType是ParameterType的父类的时候,才不会发生任何问题(形参要变只能逆变)。所以在提出逆变参数的概念。
同样地,利用这个原则也可以用来理解。
假如在某使用环境中有如下语句:
ReturnType rt = sup.doSomething(o);
假如设计变更,子类需要返回一个与ReturnType有继承关系的类NewReturnType,如果返回的是ReturnType的父类,那么则上面语句则不能通过编译,只有NewReturnType是ReturnType的子类的时候,才不会违反Liskov子类原则。

Java 泛型 协变性、逆变性的更多相关文章

  1. Java泛型的逆变

    在上篇<Java泛型的协变>这篇文章中遗留以下问题——协变不能解决将子类型添加到父类型的泛型列表中.本篇将用逆变来解决这个问题. 实验准备 我们首先增加以下方法,见代码清单1所示. 代码清 ...

  2. JAVA泛型——逆变

    在上篇<JAVA泛型——协变>这篇文章中遗留以下问题——协变不能解决将子类型添加到父类型的泛型列表中.本篇将用逆变来解决这个问题. 实验准备 我们首先增加以下方法,见代码清单1所示. 代码 ...

  3. Java泛型的协变

    在上篇<Java泛型的基本使用>这篇文章中遗留以下问题,即将子类型也能添加到父类型的泛型中,要实现这种功能必须借助于协变. 实验准备 现在在上篇文章展示的Decorator类型的基础上,增 ...

  4. JQuery选择器大全 前端面试送命题:面试题篇 对IOC和DI的通俗理解 c#中关于协变性和逆变性(又叫抗变)帮助理解

    JQuery选择器大全   jQuery 的选择器可谓之强大无比,这里简单地总结一下常用的元素查找方法 $("#myELement")    选择id值等于myElement的元素 ...

  5. 不变性、协变性和逆变性(Invariance, Covariance & Contravariance)

    源码下载 一.里氏替换原则(Liskov Substitution Principle LSP) 我们要讲的不是协变性和逆变性(Covariance & Contravariance)吗?是的 ...

  6. oc 的 协变性与逆变性

    ?协变性与逆变性是类型关系在范畴论的定义.是类型的继承关系在高阶类型中的定义? __kindof只是在统一继承体系下方便了类型转化,提供了使用时语法上的便捷:但是对于类型转换是否正确不做判定: kin ...

  7. Java泛型中的协变和逆变

    Java泛型中的协变和逆变 一般我们看Java泛型好像是不支持协变或逆变的,比如前面提到的List<Object>和List<String>之间是不可变的.但当我们在Java泛 ...

  8. Java泛型的协变与逆变

    泛型擦除 Java的泛型本质上不是真正的泛型,而是利用了类型擦除(type erasure),比如下面的代码就会出现错误: 报的错误是:both methods  have same erasure ...

  9. Java中的逆变与协变

    看下面一段代码 Number num = new Integer(1); ArrayList<Number> list = new ArrayList<Integer>(); ...

随机推荐

  1. logstash通过kafka传输nginx日志(三)

    单个进程 logstash 可以实现对数据的读取.解析和输出处理.但是在生产环境中,从每台应用服务器运行 logstash 进程并将数据直接发送到 Elasticsearch 里,显然不是第一选择:第 ...

  2. svn用Cornerstone上传项目丢失静态库.a问题的解决

    最近电脑重装系统后,装上Cornerstone上传项目后,发现又有丢失.a文件的问题,这里说一下解决办法,打开Preferences-->Subversion-->General,将Use ...

  3. Scala 中Null, None, Nothing, Nil

    转自:http://blog.csdn.net/bluejoe2000/article/details/30465175 在scala中这四个类型名称很类似,作用确实完全不同的. None是一个obj ...

  4. Filemon(Filemon文件系统监视)V7.04官方版

    软件名称:Filemon(Filemon文件系统监视)V7.04官方版 软件语言: 简体中文 授权方式: 免费软件 运行环境: Win 32位/64位 软件大小: 265KB 图片预览: 软件简介: ...

  5. iOS GCD基础篇 - 同步、异步,并发、并行的理解

    1.关于GCD - GCD全称是Grand Central Dispatch  - GCD是苹果公司为多核的并行运算提出的解决方案  - GCD会自动利用更多的CPU内核(比如双核.四核)  - GC ...

  6. HDU 5887 Herbs Gathering

    背包,$map$,优化. 和普通背包一样,$map$加一个$erase$优化一下就可以跑的很快了. #pragma comment(linker, "/STACK:1024000000,10 ...

  7. 个性化推荐系统中的BadCase分析

    针对内测用户反馈,由于前一天点击了几个动画,导致第二天推荐的动画屏占比较高,于是开始对此badcase进行分析. 首先分析了该用户的历史观看纪录,由于系统升级,日志缺陷问题,导致该用户10.15-11 ...

  8. 图片切换效果,纯js

    公司有个技术很牛x的“老腊肉”,我向他请教,他给了我个网址,上面蛮多效果的,于是乎~我决定照着效果看能不能自己敲出来,我要变大神X3,重要的事说3遍. 它完成的效果是这样的: 好吧,一步一步来,先把框 ...

  9. [河南省ACM省赛-第三届] 素数 (nyoj 169)

    #include <iostream> #include <cstdio> #include <queue> #include <cstring> #i ...

  10. jvm原理及调优

    一.java内存管理及垃圾回收 jvm内存组成结构 jvm栈由堆.栈.本地方法栈.方法区等部分组成,结构图如下所示: (1)堆 所有通过new创建的对象的内存都在堆中分配,堆的大小可以通过-Xmx和- ...