在上篇《java泛型——协变》这篇文章中遗留以下问题——协变不能解决将子类型添加到父类型的泛型列表中。本篇将用逆变来解决这个问题。
实验准备我们首先增加以下方法,见代码清单1所示。
代码清单1
/***
* 描 述:Exp3使用逆变<br/>
* 作 者:jiaan.gja<br/>
* 历 史: (版本) 作者 时间 注释 <br/>
*@paramitemList*/publicvoiddoDecorate3(List<?superT>itemList, T t) {for(inti = 0; i < itemList.size(); i++) {
System.out.PRintln(itemList.get(i));
}
}/***
* 描 述:Exp3使用逆变<br/>
* 作 者:jiaan.gja<br/>
* 历 史: (版本) 作者 时间 注释 <br/>
*@paramitemList*/publicvoidaddDecorate3(List<?superT>itemList, T t) {
itemList.add(t);
}
语法List<? super T>即为Java泛型的逆变,addDecorate3方法中的itemList.add(t)语句也没有协变出现的编译问题。
实验:泛型逆变现在我们尝试下逆变的用途,如代码清单2所示。
代码清单2
/***
* 类 名: Exp3<br/>
* 描 述: 泛型的逆变使用<br/>
* 作 者: 耿嘉安<br/>
* 创 建: 2015年8月25日<br/>
*
* 历 史: (版本) 作者 时间 注释*/classExp3 {publicstaticvoidmain(String[] args) {
Decorator<Auction> decoratorA =newDecorator<Auction>();
List<Auction> listA =newArrayList<Auction>();
Auction auctionOne=newAuction("auctionOne");
Auction auctionTwo=newAuction("auctionTwo");
decoratorA.addDecorate3(listA, auctionOne);
decoratorA.addDecorate3(listA, auctionTwo);
decoratorA.doDecorate3(listA);
Decorator<Table> decoratorB =newDecorator<Table>();
List<Table> listB =newArrayList<Table>();
Table tableOne=newTable("tableOne", 10);
Table tableTwo=newTable("tableTwo", 20);
decoratorB.addDecorate3(listB, tableOne);
decoratorB.addDecorate3(listB, tableTwo);
decoratorB.doDecorate3(listB);
Decorator<Service> decoratorC =newDecorator<Service>();
List<Service> listC =newArrayList<Service>();
Service serviceOne=newService("serviceOne", "methodOne");
Service serviceTwo=newService("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 =newArrayList<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<?superAuction> itemList =newArrayList<Object>();
List<?superAuction> itemList =newArrayList<Auction>();
List<?superTable> itemList =newArrayList<Object>();
List<?superTable> itemList =newArrayList<Auction>();
List<?superTable> itemList =newArrayList<Table>();
List<?superService> itemList =newArrayList<Object>();
List<?superService> itemList =newArrayList<Auction>();
List<?superService> itemList =newArrayList<Service>();
而下面这样是不允许的:
List<?superAuction> itemList =newArrayList<Table>();
List<?superAuction> itemList =newArrayList<Service>();
List<?superTable> itemList =newArrayList<Service>();
List<?superService> itemList =newArrayList<Table>();
现在我们编写以下代码:
List<?superAuction> 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(newAuction("auctionThr"));
itemList.add(newService("serviceThr", "methodThr"));
itemList.add(newTable("tableThr", 10));
上述代码没有问题,因为不论itemList是ArrayList<Object>或者ArrayList<Auction>,向其中添加Auction、Table、Service都符合最初的原则。
假如向itemList添加Object是编译失败的,因为itemList可能是ArrayList<Auction>。
总结如果向泛型中添加子类,可以使用逆变来实现。如果要从泛型中获取子类,逆变有转型错误,此时应该使用协变。