Java 泛型 协变性、逆变性
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 泛型 协变性、逆变性的更多相关文章
- Java泛型的逆变
在上篇<Java泛型的协变>这篇文章中遗留以下问题——协变不能解决将子类型添加到父类型的泛型列表中.本篇将用逆变来解决这个问题. 实验准备 我们首先增加以下方法,见代码清单1所示. 代码清 ...
- JAVA泛型——逆变
在上篇<JAVA泛型——协变>这篇文章中遗留以下问题——协变不能解决将子类型添加到父类型的泛型列表中.本篇将用逆变来解决这个问题. 实验准备 我们首先增加以下方法,见代码清单1所示. 代码 ...
- Java泛型的协变
在上篇<Java泛型的基本使用>这篇文章中遗留以下问题,即将子类型也能添加到父类型的泛型中,要实现这种功能必须借助于协变. 实验准备 现在在上篇文章展示的Decorator类型的基础上,增 ...
- JQuery选择器大全 前端面试送命题:面试题篇 对IOC和DI的通俗理解 c#中关于协变性和逆变性(又叫抗变)帮助理解
JQuery选择器大全 jQuery 的选择器可谓之强大无比,这里简单地总结一下常用的元素查找方法 $("#myELement") 选择id值等于myElement的元素 ...
- 不变性、协变性和逆变性(Invariance, Covariance & Contravariance)
源码下载 一.里氏替换原则(Liskov Substitution Principle LSP) 我们要讲的不是协变性和逆变性(Covariance & Contravariance)吗?是的 ...
- oc 的 协变性与逆变性
?协变性与逆变性是类型关系在范畴论的定义.是类型的继承关系在高阶类型中的定义? __kindof只是在统一继承体系下方便了类型转化,提供了使用时语法上的便捷:但是对于类型转换是否正确不做判定: kin ...
- Java泛型中的协变和逆变
Java泛型中的协变和逆变 一般我们看Java泛型好像是不支持协变或逆变的,比如前面提到的List<Object>和List<String>之间是不可变的.但当我们在Java泛 ...
- Java泛型的协变与逆变
泛型擦除 Java的泛型本质上不是真正的泛型,而是利用了类型擦除(type erasure),比如下面的代码就会出现错误: 报的错误是:both methods have same erasure ...
- Java中的逆变与协变
看下面一段代码 Number num = new Integer(1); ArrayList<Number> list = new ArrayList<Integer>(); ...
随机推荐
- Linux 下Nginx 的安装及负载均衡的简单配置
这次发布程序需要均衡负载,网上看了一下这方便的东西,觉得很不错,学完之后做下总结,一遍后期用到. 1.安装nginx之前需要安装的两个依赖,pcre-x.x.x.tar.gz 和pcre-devel- ...
- 一步步优化JVM五:优化延迟或者响应时间
本节的目标是做一些优化以满足对应用对延迟的需求.这次需要几个步骤,包括完善Java堆大小的配置,评估垃圾回收占用的时间和频率,也许还要尝试切换到不同的垃圾回收器,以及由于使用了不同的垃圾回收器,需要重 ...
- java静态方法之线程安全问题
静态方法和实例方法的区别是静态方法只能引用静态变量,静态方法通过类名来调用,实例方法通过对象实例来调用 每个线程都有自己的线程栈,栈与线程同时创建,每一个虚拟机线程都有自己的程序计数器PC,在任何时刻 ...
- 【转】Jmeter(三)-简单的HTTP请求(非录制)
首先建立一个线程组(Thread Group),为什么所有的请求都要加入线程组这个组件呢?不加不行吗?答案当然是不行的.因为jmeter的所有任务都必须由线程处理,所有任务都必须在线程组下面创建. 选 ...
- HDU 5903 Square Distance
$dp$预处理,贪心. 因为$t$串前半部分和后半部分是一样的,所以只要构造前一半就可以了. 因为要求字典序最小,所以肯定是从第一位开始贪心选择,$a,b,c,d,...z$,一个一个尝试过去,如果发 ...
- POJ 1952 BUY LOW, BUY LOWER
$dp$. 一开始想了一个$dp$做法,$dp[i][j]$表示前$i$个数字,下降序列长度为$j$的方案数为$dp[i][j]$,这样做需要先离散化然后用树状数组优化,空间复杂度为${n^2}$,时 ...
- EasyUI Messager 消息框
通过 $.messager.defaults 重写默认的 defaults. 消息框(messager)提供不同样式的消息框,包括警示(alert).确认(confirm).提示(prompt).进展 ...
- Spring Security(18)——Jsp标签
目录 1.1 authorize 1.2 authentication 1.3 accesscontrollist Spring Security也有对Jsp标签的支持的标签库 ...
- ECOS-Ecstore证书生产失效问题排查
无法生成证书问题排查 无法生成证书问题排查 author :James,jimingsong@vip.qq.com since :2015-03-02 名称解释(官方) 常见错误 1. 名称解释(官方 ...
- css margin相关问题及应用
一.margin常见问题 1.IE6下双边距问题 margin双布局可以说是IE6下经典的bug之一.产生的条件是:block元素+浮动+margin. 2.maring重叠的问题 css2.0规范对 ...