JAVA泛型——逆变
在上篇《JAVA泛型——协变》这篇文章中遗留以下问题——协变不能解决将子类型添加到父类型的泛型列表中。本篇将用逆变来解决这个问题。
实验准备
我们首先增加以下方法,见代码清单1所示。
代码清单1
/**
*
* 描 述:Exp3使用逆变<br/>
* 作 者:jiaan.gja<br/>
* 历 史: (版本) 作者 时间 注释 <br/>
* @param itemList
*/
public void doDecorate3(List<? super T> itemList, T t) {
for(int i = 0; i < itemList.size(); i++) {
System.out.println(itemList.get(i));
}
} /**
*
* 描 述:Exp3使用逆变<br/>
* 作 者:jiaan.gja<br/>
* 历 史: (版本) 作者 时间 注释 <br/>
* @param itemList
*/
public void addDecorate3(List<? super T> itemList, T t) {
itemList.add(t);
}
语法List<? super T>即为Java泛型的逆变,addDecorate3方法中的itemList.add(t)语句也没有协变出现的编译问题。
实验:泛型逆变
现在我们尝试下逆变的用途,如代码清单2所示。
代码清单2
/**
*
* 类 名: Exp3<br/>
* 描 述: 泛型的逆变使用<br/>
* 作 者: 耿嘉安<br/>
* 创 建: 2015年8月25日<br/>
*
* 历 史: (版本) 作者 时间 注释
*/
class Exp3 {
public static void main(String[] args) { Decorator<Auction> decoratorA = new Decorator<Auction>();
List<Auction> listA = new ArrayList<Auction>();
Auction auctionOne = new Auction("auctionOne");
Auction auctionTwo = new Auction("auctionTwo");
decoratorA.addDecorate3(listA, auctionOne);
decoratorA.addDecorate3(listA, auctionTwo);
decoratorA.doDecorate3(listA); Decorator<Table> decoratorB = new Decorator<Table>();
List<Table> listB = new ArrayList<Table>();
Table tableOne = new Table("tableOne", 10);
Table tableTwo = new Table("tableTwo", 20);
decoratorB.addDecorate3(listB, tableOne);
decoratorB.addDecorate3(listB, tableTwo);
decoratorB.doDecorate3(listB); Decorator<Service> decoratorC = new Decorator<Service>();
List<Service> listC = new ArrayList<Service>();
Service serviceOne = new Service("serviceOne", "methodOne");
Service serviceTwo = new Service("serviceTwo", "methodTwo");
decoratorC.addDecorate3(listC, serviceOne);
decoratorC.addDecorate3(listC, serviceTwo);
decoratorC.doDecorate3(listC); /**
* 测试逆变开始
*/
decoratorA.doDecorate3(listB);
decoratorA.doDecorate3(listC); }
}
我们发现以下两条语句都会编译出错:
decoratorA.doDecorate3(listB);
decoratorA.doDecorate3(listC);
我们暂且将这个问题放一放,将它们暂时注释掉,增加代码清单3中的代码。
代码清单3
List<Object> listD = new ArrayList<Object>(); decoratorA.doDecorate3(listD);
decoratorA.doDecorate3(listA);
decoratorA.doDecorate3(listB);
decoratorA.doDecorate3(listC);
decoratorB.doDecorate3(listD);
decoratorB.doDecorate3(listA);
decoratorB.doDecorate3(listB);
decoratorB.doDecorate3(listC);
decoratorC.doDecorate3(listD);
decoratorC.doDecorate3(listA);
decoratorC.doDecorate3(listC);
decoratorC.doDecorate3(listB);
代码清单3中,decoratorA.doDecorate3(listB);decoratorA.doDecorate3(listC);decoratorB.doDecorate3(listC);decoratorC.doDecorate3(listB);这四条语句编译错误,这说明如下声明是允许的:
List<? super Auction> itemList = new ArrayList<Object>();
List<? super Auction> itemList = new ArrayList<Auction>();
List<? super Table> itemList = new ArrayList<Object>();
List<? super Table> itemList = new ArrayList<Auction>();
List<? super Table> itemList = new ArrayList<Table>();
List<? super Service> itemList = new ArrayList<Object>();
List<? super Service> itemList = new ArrayList<Auction>();
List<? super Service> itemList = new ArrayList<Service>();
而下面这样是不允许的:
List<? super Auction> itemList = new ArrayList<Table>();
List<? super Auction> itemList = new ArrayList<Service>();
List<? super Table> itemList = new ArrayList<Service>();
List<? super Service> itemList = new ArrayList<Table>();
现在我们编写以下代码:
List<? super Auction> itemList = listA;
Object o = itemList.get(0);
Auction a = itemList.get(0);
Table t = itemList.get(0);
Service s = itemList.get(0);
itemList = listD;
o = itemList.get(0);
a = itemList.get(0);
t = itemList.get(0);
s = itemList.get(0);
上述代码中,除了从itemList获取Object的语句之外,都会编译出错,这是为什么?因为itemList有可能是一个ArrayList<Object>,所以转型为Auction是可能错误的。itemList可能是ArrayList<Object>或者ArrayList<Auction>,所以转型为Table或者Service必然是不对的。
最后我们增加以下代码:
itemList.add(new Auction("auctionThr"));
itemList.add(new Service("serviceThr", "methodThr"));
itemList.add(new Table("tableThr", 10));
上述代码没有问题,因为不论itemList是ArrayList<Object>或者ArrayList<Auction>,向其中添加Auction、Table、Service都符合最初的原则。
假如向itemList添加Object是编译失败的,因为itemList可能是ArrayList<Auction>。
总结
如果向泛型中添加子类,可以使用逆变来实现。如果要从泛型中获取子类,逆变有转型错误,此时应该使用协变。
如需转载,请标明本文作者及出处——作者:jiaan.gja,本文原创首发:博客园,原文链接:http://www.cnblogs.com/jiaan-geng/p/4920068.html
JAVA泛型——逆变的更多相关文章
- .NET 4.0中的泛型逆变和协变
转载自:http://www.cnblogs.com/Ninputer/archive/2008/11/22/generic_covariant.html:自己加了一些理解 随Visual Studi ...
- java协变逆变,PECS
public static void main(String[] args) { // Object <- Fruit <- Apple <- RedApple System.out ...
- JAVA泛型——协变
在上篇<JAVA泛型——基本使用>这篇文章中遗留以下问题,即将子类型Table或者也能添加到父类型Auction的泛型中,要实现这种功能必须借助于协变. 实验准备 现在在<JAVA泛 ...
- Java泛型的逆变
在上篇<Java泛型的协变>这篇文章中遗留以下问题——协变不能解决将子类型添加到父类型的泛型列表中.本篇将用逆变来解决这个问题. 实验准备 我们首先增加以下方法,见代码清单1所示. 代码清 ...
- Java 逆变与协变
最近一直忙于学习模电.数电,搞得头晕脑胀,难得今天晚上挤出一些时间来分析一下Java中的逆变.协变.Java早于C#引入逆变.协变,两者在与C#稍有不同,Java中的逆变.协变引入早于C#,故在形式没 ...
- Java中的协变与逆变
Java作为面向对象的典型语言,相比于C++而言,对类的继承和派生有着更简洁的设计(比如单根继承). 在继承派生的过程中,是符合Liskov替换原则(LSP)的.LSP总结起来,就一句话: 所有引用基 ...
- .NET泛型03,泛型类型的转换,协变和逆变
协变(Convariant)和逆变(Contravariant)的出现,使数组.委托.泛型类型的隐式转换变得可能. 子类转换成基类,称之为协变:基类转换成子类,称之为逆变..NET4.0以来,支持了泛 ...
- .NET中的逆变协变
MSDN上的说法: 协变和逆变都是术语,前者指能够使用比原始指定的派生类型的派生程度更小(不太具体的)的类型,后者指能够使用比原始指定的派生类型的派生程度更大(更具体的)的类型----------(注 ...
- 泛型类型的协变(covariant)和逆变
官网:http://msdn.microsoft.com/zh-cn/library/dd799517.aspx 原文链接:http://book.51cto.com/art/201112/30857 ...
随机推荐
- bootstrap-table简单使用
开发项目时总想着能不能有一款插件包含分页,查询等常用功能,后来发现了bootstrap-table 直接看代码和效果图 <!DOCTYPE html> <html lang=&quo ...
- .net core执行dotnet ef migrations createmodel等命令出错
.net core执行dotnet ef migrations createmodel等命令出错 执行dotnet ef migrations createmodel.dotnet ef migrat ...
- [C#学习笔记]分部类和分部方法
知识在于积累. 前言 好久没写博客了,因为在看<CLR via C#>的时候,竟然卡在了分部方法这一小节几天没下去.今天重新认真阅读,有些感悟,所以在此记录. 然后. 每天早晨第一句,&l ...
- 经典的兔子生兔子问题(C#递归解法)
古典问题:有一对兔子,从出生后第3个月起每个月都生一对兔子,小兔子长到第三个月后每个月又生一对兔子,假如兔子都不死,问每个月的兔子总数为多少? 思路:先求出每个月新增的兔子,再用循环求和即可算出这个月 ...
- 遍历datatable的几种方法(C# )
转载 遍历datatable的方法2009-09-08 10:02方法一: DataTable dt = dataSet.Tables[0]; for(int i = 0 ; i ...
- swagger简单配置
第一步: 在nuget.org中查找Swashbuckle并下载 在nuget.org中查找Swagger.net.UI,并下载 第二步: 下载完之后,App_Start多了三个文件 Swagger. ...
- sqlserver常用函数
1.字符串函数 --ascii函数,返回字符串最左侧字符的ascii码值 SELECT ASCII('dsd') AS asciistr --ascii代码转换函数,返回指定ascii值对应的字符 ) ...
- Android Library 使用规则
1. 一个项目必须声明为 is library.使用该 library 的项目导入该 library. 2. library 的 manifect 中 activity 的声明需明确包名以避免找不到. ...
- Cookie背景了解
Cookie的复数形态是Cookies, 英文的意思是小甜饼,小饼干. 类型为小型文本文件, 指某些网站为了辨别用户身份储存在用户本地中断上的数据. 是前网景公司的员工 卢-蒙特利在1993年3月发明 ...
- shell脚本报错:-bash: xxx: /bin/bash^M: bad interpreter: No such file or directory
当我们把文件从windows系统中编辑的文件拷贝到linux系统中,如果我们执行文件会保存如下的错: shell脚本报错:-bash: xxx: /bin/bash^M: bad interprete ...