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>。

总结

如果向泛型中添加子类,可以使用逆变来实现。如果要从泛型中获取子类,逆变有转型错误,此时应该使用协变。

时间: 2024-11-07 13:21:14

Java泛型的逆变的相关文章

Java中的逆变与协变(转)

看下面一段代码 Number num = new Integer(1); ArrayList<Number> list = new ArrayList<Integer>(); //type mismatch List<? extends Number> list = new ArrayList<Number>(); list.add(new Integer(1)); //error list.add(new Float(1.2f)); //error 有人会

Java中的逆变与协变

看下面一段代码 Number num = new Integer(1); ArrayList<Number> list = new ArrayList<Integer>(); //type mismatch List<? extends Number> list = new ArrayList<Number>(); list.add(new Integer(1)); //error list.add(new Float(1.2f)); //error 有人会

Java中的逆变和协变

// public final class Integer extends Number Number num = new Integer(1); List<Number> list = new ArrayList<>(); list.add(new Integer(3)); ArrayList<Number> list = new ArrayList<Integer>(); //type mismatch List<? extends Number&

Java中的逆变与协变 很直接不饶弯的讲出来了

http://blog.csdn.net/z69183787/article/details/51598345 看下面一段代码: Number num = new Integer(1); List<Number> list = new ArrayList<>(); list.add(new Integer(3)); ArrayList<Number> list = new ArrayList<Integer>(); //type mismatch List&

泛型之逆变和协变总结

泛型之逆变和协变总结 c# 泛型 逆变 协变 变的概念 协变(Foo<父类> = Foo<子类> ) 逆变(Foo<子类> = Foo<父类>) 逆变与协变的相互作用 变的概念 //父类 = 子类 string str = "string";  object obj = str;//变了  协变(Foo<父类> = Foo<子类> ) //泛型委托: public delegate T MyFuncA<T&g

ios开发ios9新特性关键字学习:泛型,逆变,协变,__kindof

一:如何去学习?都去学习什么? 1:学习优秀项目的设计思想,多问几个为什么,为什么要这么设计,这么设计的好处是什么,还能不能在优化 ,如何应用到自己的项目中 2:学习优秀项目的代码风格,代码的封装设计思想,为什么要这么设计,这么设计的好处是什么,还能不能在优化 ,如何应用到自己的项目中,每行代码都要用心去写,每一行代码都要力求使最简洁的 3:学习别人遇到问题是如何分析问题,解决问题的方法是什么 4:遇到新东西应该如何去学习:1:先研究要学习的东西作用是什么 ,有什么好处  2:如何使用:具体的语

解读经典《C#高级编程》最全泛型协变逆变解读 页127-131.章4

前言 本篇继续讲解泛型.上一篇讲解了泛型类的定义细节.本篇继续讲解泛型接口. 泛型接口 使用泛型可定义接口,即在接口中定义的方法可以带泛型参数.然后由继承接口的类实现泛型方法.用法和继承泛型类基本没有区别. 不变.协变和逆变 在.Net4.0之前,泛型接口是不变的..Net4.0通过协变和逆变为泛型接口和泛型委托增加了重要的扩展. 注:本书总体非常好,但在协变和逆变方面,我认为是有缺陷的.我一直偏好通过读书籍来了解技术,而不是逛论坛,但协变和逆变的问题我研究了本书多次,都没搞懂而放弃了,反正平时

c# 泛型协变逆变学习

最近在项目开发当中使用泛型委托Func较多,查看Func的定义就会发现Func的入参都会都会标记上in,出参都会标记上out. in 和out和泛型类型实参有关, 其中in代表逆变,out代表协变.自己协变和逆变在设计接口或者委托的时候也没有定义过, 因此就详细了解一下其用法. 一.关于协变和逆变 在c# 4.0后,泛型类型参数分为以下三种请情况 1.不变量   这带便泛型参数类型参数不能更改 2.协变量  泛型类型参数可以从一个类更改为它的某个基类.c#中使用out关键字标记协变量形式的泛型类

JAVA泛型——逆变

在上篇<JAVA泛型——协变>这篇文章中遗留以下问题——协变不能解决将子类型添加到父类型的泛型列表中.本篇将用逆变来解决这个问题. 实验准备 我们首先增加以下方法,见代码清单1所示. 代码清单1 /** * * 描 述:Exp3使用逆变<br/> * 作 者:jiaan.gja<br/> * 历 史: (版本) 作者 时间 注释 <br/> * @param itemList */ public void doDecorate3(List<? supe