zoukankan      html  css  js  c++  java
  • JAVA泛型——协变

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

    实验准备

      现在在《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泛型——逆变》。

    总结

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

    如需转载,请标明本文作者及出处——作者:jiaan.gja,本文原创首发:博客园,原文链接:http://www.cnblogs.com/jiaan-geng/p/4916347.html 
  • 相关阅读:
    URLs
    上班确实累!!!
    转: java 双向map
    HttpReader
    QQ龙虎榜数据接口
    简易行情界面
    下载新浪的行情数据
    淘宝上的大智慧L2数据,月卡最便宜是8元钱,这个也可以获取BBD、DDX等数据!
    获取历史K线数据的几个方法
    好久不写博了.
  • 原文地址:https://www.cnblogs.com/jiaan-geng/p/4916347.html
Copyright © 2011-2022 走看看