zoukankan      html  css  js  c++  java
  • 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>。

    总结

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

  • 相关阅读:
    es5预览本地文件、es6练习代码演示案例
    Java实现 LeetCode 838 推多米诺(暴力模拟)
    Java实现 LeetCode 838 推多米诺(暴力模拟)
    Java实现 LeetCode 838 推多米诺(暴力模拟)
    Java实现 LeetCode 837 新21点(DP)
    Java实现 LeetCode 837 新21点(DP)
    Java实现 LeetCode 837 新21点(DP)
    Java实现 LeetCode 836 矩形重叠(暴力)
    Subversion under Linux [Reprint]
    Subversion how[Reprint]
  • 原文地址:https://www.cnblogs.com/jiaan-geng/p/4848630.html
Copyright © 2011-2022 走看看