簡明理解泛型中的? extends T 與 ? super T

簡單來說,通過List<? extends T>定義的list只能從裏面取數據,取出來的對象都是T類型或者T類型的子類。
對於List<? super T>定義的list,只能將對象元素放入list,放入的對象是T類型或者T類型的子類。

總結爲PECS原則:由於<? extends T>的只能取,不能存,而<? super T>得只能存,不能取,因此就有producer用<? extends T>,consumer用<? super T>。

比如有3個類的繼承關係如下:


public class MySuperObject {
    public void superMethod(){
        System.out.println("super method " +this);
    }
}

public class MyObject extends MySuperObject{
    public void myMethod(){
        System.out.println("myMethod " + this);
    }
}


public class MyChildObject extends MyObject{
    public void childMethod(){
        System.out.println("childMethod " + this);
    }
}

對應List<? extends T>使用場景爲:


public class TestExtends {

    public static void testObjExtendsList(List<? extends MyObject> objs){
        //? extends MyObject 不能添加
        //numbers.add(new MyObject());
        //因爲對象類型一定是MyObject或者MyObject的子類,所以可以調用MyObject或者MyObject父類的方法
        objs.forEach( obj -> {
            obj.superMethod();
            obj.myMethod();
        });
    }

    public static void main(String[] args){
        List<MyObject> objs = new ArrayList<>();
        objs.add(new MyObject());
        objs.add(new MyChildObject());
        testObjExtendsList(objs);

        List<MyChildObject> childObjs = new ArrayList<>();
        childObjs.add(new MyChildObject());
        childObjs.add(new MyChildObject());
        testObjExtendsList(childObjs);
    }
}

List<? super T>使用場景:


public class TestSuper {

    public static void  testSuperList(List<? super MyObject> objs){
        //只要是MyObject或者MyObject的子類都可以放進List
        objs.add(new MyObject());
        objs.add(new MyChildObject());
        //#1 不能放MyObject的父類
        //objs.add(new MySuperObject());
        //#2 不能從List取元素,取出來的元素類型不確定
        //objs.forEach( obj -> System.out.println(obj));
    }
    public static void main(String[] args){

        //List<? super MyObject> 表示裏面的元素可以當做MyObject或者MyObject的父類來使用,
        //MyObject或者MyObject的子類都是可以轉型爲MyObject或者MyObject的父類
        List<MyObject> objs = new ArrayList<>();
        testSuperList(objs);

        objs.forEach( obj -> {
            obj.superMethod();
            obj.myMethod();
        });

        List<MySuperObject> superObjs = new ArrayList<>();
        testSuperList(superObjs);

        superObjs.forEach( obj -> {
            obj.superMethod();
            //obj.myMethod();
        });
    }
}

如何理解producer用<? extends T>,consumer用<? super T>,如上所示:
testObjExtendsList方法的調用者爲producer,因爲需要準備數據(放入數據到list)。
testSuperList方法的調用者爲consumer,因爲是數據使用者(從list取數據)。

JDK底層源碼用到的地方:

java.util.Collections.copy(List<? super T> dest, List<? extends T> src)

    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());
            }
        }
    }
	......
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章