? extend
1 List<Apple> apples = new ArrayList<Apple>();
2 List<? extends Fruit> fruits = apples;
3 fruits.add(new Strawberry());
這次县好,代碼就編譯不過去了癞谒!Java編譯器會阻止你往一個Fruit list里加入strawberry。在編譯時我們就能檢測到錯誤蠢络,在運行時就不需要進行檢查來確保往列表里加入不兼容的類型了。即使你往list里加入Fruit對象也不行:
1 fruits.add(new Fruit());
你沒有辦法做到這些迟蜜。事實上你不能夠往一個使用了? extends的數據結構里寫入任何的值刹孔。
原因非常的簡單,你可以這樣想:這個? extends T 通配符告訴編譯器我們在處理一個類型T的子類型娜睛,但我們不知道這個子類型究竟是什么髓霞。因為沒法確定,為了保證類型安全畦戒,我們就不允許往里面加入任何這種類型的數據方库。另一方面,因為我們知道障斋,不論它是什么類型纵潦,它總是類型T的子類型,當我們在讀取數據時垃环,能確保得到的數據是一個T類型的實例:
1 Fruit get = fruits.get(0);
? super
使用 ? super 通配符一般是什么情況酪穿?讓我們先看看這個:
1 List<Fruit> fruits = new ArrayList<Fruit>();
2 List<? super Apple> = fruits;
我們看到fruits指向的是一個裝有Apple的某種超類(supertype)的List。同樣的晴裹,我們不知道究竟是什么超類,但我們知道Apple和任何Apple的子類都跟它的類型兼容救赐。既然這個未知的類型即是Apple涧团,也是GreenApple的超類,我們就可以寫入:
1 fruits.add(new Apple());
2 fruits.add(new GreenApple());
如果我們想往里面加入Apple的超類经磅,編譯器就會警告你:
1 fruits.add(new Fruit());
2 fruits.add(new Object());
因為我們不知道它是怎樣的超類泌绣,所有這樣的實例就不允許加入。
從這種形式的類型里獲取數據又是怎么樣的呢预厌?結果表明阿迈,你只能取出Object實例:因為我們不知道超類究竟是什么,編譯器唯一能保證的只是它是個Object轧叽,因為Object是任何Java類型的超類苗沧。
存取原則和PECS法則
總結 ? extends 和 the ? super 通配符的特征刊棕,我們可以得出以下結論:
如果你想從一個數據類型里獲取數據,使用 ? extends 通配符
如果你想把對象寫入一個數據結構里待逞,使用 ? super 通配符
如果你既想存甥角,又想取,那就別用通配符识樱。