zoukankan      html  css  js  c++  java
  • 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> list = new ArrayList<Number>();  
    list.add(new Integer(1)); //error  

    为什么Number的对象可以由Integer实例化,而ArrayList<Number>的对象却不能由ArrayList<Integer>实例化?

    list中的<? extends Number>声明其元素是Number或Number的派生类,为什么不能add Integer?

    为了解决这些问题,需要了解Java中的逆变和协变以及泛型中通配符用法。

    1、逆变和协变

      Java中String类型是继承自Object的,姑且记做String ≦ Object,表示String是Object的子类型,String的对象可以赋给Object的对象。而Object的数组类型Object[],理解成是由Object构造出来的一种新的类型,可以认为是一种构造类型,记f(Object),那么可以这么来描述协变和逆变:
           当A ≦ B时,如果有f(A) ≦ f(B),那么f叫做协变;
           当A ≦ B时,如果有f(B) ≦ f(A),那么f叫做逆变;
           如果上面两种关系都不成立则叫做不可变。

    2、泛型中的通配符实现逆变和协变

      JAVA中泛型是不变的,可有时需要实现逆变与协变,怎么办呢?这时就需要通配符?。
           <? extends>实现了泛型的协变,比如:

     

     List<? extends Number> list = new ArrayList<>();  

      “? extends Number”则表示通配符”?”的上界为Number,换句话说就是,

      “? extends Number”可以代表Number或其子类,但代表不了Number的父类(如Object),因为通配符的上界是Number。
           于是有“? extends Number” ≦ Number,则List<? extends Number> ≦ List< Number >。那么就有:
      

    List<? extends Number> list001 = new ArrayList<Integer>();  
    List<? extends Number> list002 = new ArrayList<Float>();  

     但是这里不能向list001、list002添加除null以外的任意对象。可以这样理解一下,List<Integer>可以添加Interger及其子类,List<Float>可以添加Float及其子类,List<Integer>、List<Float>都是List<? extends Animal>的子类型,如果能将Float的子类添加到List<? extends Animal>中,就说明Float的子类也是可以添加到List<Integer>中的,显然是不可行。故java为了保护其类型一致,禁止向List<? extends Number>添加任意对象,不过却可以添加null。

    <? super>实现了泛型的逆变,比如:

    List<? super Number> list = new ArrayList<>();  

     “? super Number” 则表示通配符”?”的下界为Number。为了保护类型的一致性,因为“? super Number”可以是Object或其他Number的父类,因无法确定其类型,也就不能往List<? super Number >添加Number的任意父类对象。但是可以向List<? super Number >添加Number及其子类。

    List<? super Number> list001 = new ArrayList<Number>();  
    List<? super Number> list002 = new ArrayList<Object>();  
    list001.add(new Integer(3));  
    list002.add(new Integer(3));  

    PECS原则
      生产者(Product)使用extends,消费者(Consumer)使用super。
     生产者使用extends
      如果需要一个列表提供T类型的元素(即想从列表中读取T类型的元素),需要把这个列表声明成<? extends T>,

      但是不能在该列表中添加任何元素。
     消费者使用super
      如果需要一个列表使用T类型的元素(即想把T类型的元素加入到列表中),需要把这个列表声明成<? super T>

      比如List<? super Integer>,不能保证从中读取到的元素类型。
     即是生产者也是消费者
        如果一个列表即要生产又要消费,不能使用泛型通配符声明列表

    Java.util.Collections的copy方法(JDK1.7)完美诠释了PESC

    public static <T> void copy(List<? super T> dest, List<? extends T> src) {  
        int srcSize = src.size();  
        if (srcSize > dest.size())  
            throw new IndexOutOfBoundsException("Source does not fit in dest");  
      
        if (srcSize < COPY_THRESHOLD ||  
            (src instanceof RandomAccess && dest instanceof RandomAccess)) {  
            for (int i=0; i<srcSize; i++)  
                dest.set(i, src.get(i));  
        } else {  
            ListIterator<? super T> di=dest.listIterator();  
            ListIterator<? extends T> si=src.listIterator();  
            for (int i=0; i<srcSize; i++) {  
                di.next();  
                di.set(si.next());  
            }  
        }  
    }  

    参考:https://blog.csdn.net/zero__007/article/details/52245475

  • 相关阅读:
    dmo4解析xml
    myeclise生成webservice客户端代码
    华为QOS原理及配置
    【转载】 Jointwave零延时视频传输for FPGA/ASIC进入军工领域
    【转载】 网络性能测试工具
    【转载】 结构体大小计算
    【转载】 H264的I/P/B帧类型判断
    【转载】 IP实时传输协议RTP/RTCP详解
    【转载】 了解实时媒体的播放(RTP/RTCP 和 RTSP)
    【转载】 CSDN博客与博客园使用对比
  • 原文地址:https://www.cnblogs.com/leilong/p/8638441.html
Copyright © 2011-2022 走看看