zoukankan      html  css  js  c++  java
  • Java泛型的协变

      在上篇《Java泛型的基本使用》这篇文章中遗留以下问题,即将子类型也能添加到父类型的泛型中,要实现这种功能必须借助于协变。

    实验准备

      现在在上篇文章展示的Decorator类型的基础上,增加一些代码,如代码清单1所示。

    代码清单1

    /**
         * 
         * 描 述:Exp2使用br/>
         * 作 者:jiaan.gja<br/>
         * 历 史: (版本) 作者 时间 注释 <br/>
         * @param itemList
         */
        public void doDecorate2(List<? extends T> itemList) {
            for(int i = 0; i < itemList.size(); i++) {
                System.out.println(itemList.get(i));
            }
        }
        
        /**
         * 
         * 描 述:Exp2使用<br/>
         * 作 者:jiaan.gja<br/>
         * 历 史: (版本) 作者 时间 注释 <br/>
         * @param itemList
         * @param t
         */
        public void addDecorate2(List<? extends T> itemList, T t) {
        // itemList.add(t);
        }

    可以看到这跟《Java泛型的基本使用》中的doDecorate和addDecorate方法几乎一样,唯一的区别是将List<T>改为List<? extends T>,这种语法被称为协变。如果你已经在IDE里写了上面的代码,肯定发现addDecorate2中的语句itemList.add(t);已经编译报错。所以我们先将itemList.add(t);这条语句暂时注释掉,先来看看doDecorate2有没有达到我们的预期。

    实验:泛型协变

      现在我们尝试下协变的用途,即调用doDecorate2方法,如代码清单2所示。

    代码清单2

    /**
     * 
     * 类 名: Exp2<br/>
     * 描 述: 泛型的协变使用<br/>
     * 作 者: jiaan.gja<br/>
     * 创 建: 2015年8月20日<br/>
     *
     * 历 史: (版本) 作者 时间 注释
     */
    class Exp2 {
        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.addDecorate2(listA, auctionOne);
            decoratorA.addDecorate2(listA, auctionTwo);
            decoratorA.doDecorate2(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.addDecorate2(listB, tableOne);
            decoratorB.addDecorate2(listB, tableTwo);
            decoratorB.doDecorate2(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.addDecorate2(listC, serviceOne);
            decoratorC.addDecorate2(listC, serviceTwo);
            decoratorC.doDecorate2(listC);
            
            /*
             * 协变测试开始
             */
            decoratorA.doDecorate2(listB);
            decoratorA.doDecorate2(listC);
            
        }
    }

    首先,我们看到doDecorate2方法至少能达到《Java泛型的基本使用》中的doDecorate方法的效果。

    由于声明了decoratorA的类型是Decorator<Auction>,那么此时实例decoratorA的doDecorate2的参数可以认为是下面这样:

    List<? extends Auction> itemList

    而listB的类型是ArrayList<Table>,listC的类型是ArrayList<Service>,也就是说协变允许:

    List<? extends Auction> itemList = new ArrayList<Auction>();
    List<? extends Auction> itemList = new ArrayList<Table>();
    List<? extends Auction> itemList = new ArrayList<Service>();

    这说明itemList中既可以存储Auction,也可以存储Table和Service。我们做个假设,当你想从itemList中获取Table的时候,Java编译器怀疑这实际可能是个Service,因此会有编译错误。如果想从itemList中获取Auction是没有问题的,因为无论是Table还是Service它们都可以被自动转换为父类Auction。这些推测,可以通过代码清单3验证。

    代码清单3

            List<? extends Auction> itemList = listA;
            Auction a = itemList.get(0);
            Table t = itemList.get(0);
            Service s = itemList.get(0);
            itemList = listB;
            a = itemList.get(0);
            t = itemList.get(0);
            s = itemList.get(0);
            itemList = listC;
            a = itemList.get(0);
            t = itemList.get(0);
            s = itemList.get(0);

    同样的道理,当你想要向itemList中添加Table时,编译器怀疑itemList实际是个ArrayList<Service>,因此是不允许的。在获取Auction时是没有问题的,那么添加Auction会不会也可以?由于itemList可能是ArrayList<Service>或者ArrayList<Table>,根据上一篇《Java泛型的基本使用》的结论,这实际也是不可以的。可以用下列代码验证:

            Auction auctionThree = new Auction("auctionThree");
            itemList.add(auctionThree);
            Auction tableThree = new Auction("tableThree");
            itemList.add(tableThree);
            Auction serviceThree = new Auction("serviceThree");
            itemList.add(serviceThree);

    因此这也说明了刚开始看到的addDecorate2中的语句itemList.add(t);为什么会编译报错。虽然如此,我们依然希望能将子类型添加到父类型声明的泛型中,这该怎么办?Java当然也考虑到这个问题,请看下一篇《Java泛型的逆变》

    总结

      如果从泛型中获取子类,应该使用协变。

  • 相关阅读:
    JavaScript 为字符串添加样式 【每日一段代码80】
    JavaScript replace()方法 【每日一段代码83】
    JavaScript for in 遍历数组 【每日一段代码89】
    JavaScript 创建用于对象的模板【每日一段代码78】
    html5 css3 新元素简单页面布局
    JavaScript Array() 数组 【每日一段代码88】
    JavaScript toUTCString() 方法 【每日一段代码86】
    位运算
    POJ 3259 Wormholes
    POJ 3169 Layout
  • 原文地址:https://www.cnblogs.com/jiaan-geng/p/4846208.html
Copyright © 2011-2022 走看看