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 ...
随机推荐
- linux free命令下 cached占用很大
# 背景 使用free -h命令,展示如下: # 解决方法 先执行sync命令,同步数据 然后执行 echo 1 > /proc/sys/vm/drop_caches echo 2 > / ...
- k8s 入门系列之集群安装篇
关于kubernetes组件的详解介绍,请阅读上一篇文章<k8s入门系列之介绍篇> Kubernetes集群安装部署 •Kubernetes集群组件: - etcd 一个高可用的K/V键值 ...
- OpenStack 业务链networking-sfc介绍 (1) - 概述
原文链接:https://blog.csdn.net/bc_vnetwork/article/details/65630355 1. Service Function Chain概述 Neutron ...
- Exp1 PC平台逆向破解 20164323段钊阳
实验目标 学习两种方法运行代码片段,并学习如何注入运行任何Shellcode. 三个实验内容如下: 1.手工修改可执行文件,改变程序执行流程,直接跳转到getshell函数 2.利用foo函数的bof ...
- telnet 工具
关于 telnet 命令 telnet命令通常用来远程登录.telnet程序是基于TELNET协议的远程登录客户端程序.Telnet协议是TCP/IP协议族中的一员,是Internet远程登陆服务的标 ...
- MySQL数据库密码破解
研究MySQL数据库的加解密方式,在网络攻防过程中具有重要的意义:试想一旦获取了网站一定的权限后,如果能够获取MySQL中保存用户数据,通过解密后,即可通过正常途径来访问数据库:一方面可以直接操作数据 ...
- 为解决Samba windows 无法访问 尝试过的方法
1, 通过 vi /etc/sysconfig/selinux 把 SELINUX=enforcing 修改为SELINUX= disable 退出保存,并且重启.(设置了) 2, 把wind ...
- C语言多线程编程一
1. Windows下同时打开多个对话框: #include <Windows.h> #include <process.h> //创建线程 void runmsg(void ...
- iOS 之新特性界面
1.什么事新特性界面? 新特性界面就是第一次下载程序出现的界面,他的用途是帮助用户快速了解这款APP,所有说还是很有必要学一下的. 2.如何实现新特性界面? 实现思路:从本质上看,新特性界面就是一个全 ...
- vue.js - axios Get、Post方法传参给 .net core webapi。
一:axios中是 Get请求: 1:在vue项目中通过params属性携带数据: let _self = this; axios({ method:'get', url:'http://localh ...