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

    总结

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

  • 相关阅读:
    vue之插槽
    微信公众号-关注和取消关注
    微信公众号-消息响应
    微信公众号-验证接入
    微信公众号-开发工具natapp内网穿透安装和使用
    windows安装PHP5.4+Apache2.4+Mysql5.5
    php各种主流框架的优缺点总结
    php框架的特性总结
    什么是php?php的优缺点有哪些?与其它编程语言的优缺点?
    二进制、八进制、十进制、十六进制之间转换
  • 原文地址:https://www.cnblogs.com/jiaan-geng/p/4848630.html
Copyright © 2020-2023  润新知