學會Java泛型系列(五):通配符

協(xié)變與逆變

關于協(xié)變與逆變,百度百科是這樣解釋的:

在一門程序設計語言的類型系統(tǒng)中,一個類型規(guī)則或者類型構造器是:

  • 協(xié)變(covariant),如果它保持了子類型序關系≦。該序關系是:子類型≦基類型。
  • 逆變(contravariant),如果它逆轉了子類型序關系。
  • 不變(invariant),如果上述兩種均不適用。

。。。一臉懵逼

簡單來說,如果父類型序可以代替子類型序的就叫協(xié)變,子類型序可有代替父類型序就叫逆變。
Java中數組是支持協(xié)變的

class Animal{}
class Dog extends Animal{}
class Bird extends Animal{}

Animal[] animals = new Dog[5]; //數組支持協(xié)變,Dog是Animal的子類,Animal[] 可以代替Dog[]
animals[0] = new Dog();
animals[1] = new Animal(); //編譯器通過,但運行時拋出ArrayStoreException
animals[2] = new Bird();//編譯器通過,但運行時拋出ArrayStoreException

由于Java數組是支持協(xié)變的,Dog是Animal的子類,Animal[] 可以代替Dog[],編譯器不會拒絕Animal[] 放Animal或者他的子類,但animals實際上是Dog[],往里面放Animal或者
Bird就會有問題。所以我們在寫代碼的時候也不應該使用這種方法,容易出錯,我們可以使用泛型把這種錯誤檢查提前到編譯期。

List<Animal> animals = new ArrayList<Dog>();//我們都知道是編譯不過去的

Java的泛型不支持協(xié)變,想要在繼承類間建立向上轉型關系可以使用通配符

子類型通配符

可以把Animal子類的List賦值給List<? extends Animal>,List<? extends Animal>表示從Animal繼承的類的列表。注意,List<? extends Animal>不像Animal[],List<? extends Animal>表示某一個特定的子類的集合,但不知道是具體的哪個,List<? extends Animal>中只有Dog或者只有Bird,不會Dog和Bird都有。

    List<? extends Animal> animals = new ArrayList<Dog>();
    animals.add(new Dog());  //編譯器不允許
    animals.add(new Animal()); //編譯器不允許
    animals.add(new Bird()); //編譯器不允許
    animals.add(new Object());//編譯器不允許
    animals.add(null);//編譯器通過

    Dog dog = animals.get(0); //編譯器不允許
    Animal animal = animals.get(0); //編譯器通過

可以看到List<? extends Animal>除了添加null,其他任何類型都不能加,或許你會覺得疑惑這東西有什么用,后面會有一個專題通配符的高級用法的例子。
當泛型對象聲明為子類通配類型引用時,他的多有方法有類型參數的參數都拒絕傳參,就像List的add(E e)方法一樣

超類通配符

超類通配符是指子類型序可以接受父類型序,用<? super class>表示

class Animal{}
class Dog extends Animal{}
class HotDog extends Dog{}
class Bird extends Animal{}

Holder<? super Dog> dogs = new Holder<Animal>();

dogs.set(new Animal()); //編譯器不允許
dogs.set(new Dog());      //編譯器通過
dogs.set(new HotDog());//編譯器通過
dogs.set(new Bird());     //編譯器不允許

Animal animal = dogs.get();   //編譯器不允許
Dog dog = dogs.get();            //編譯器不允許
HotDog hotDog = dogs.get(); //編譯器不允許

? super Dog表示T是Dog或者其父類,T可以賦值為Dog或者其父類,也就是在一些set(T a)、add(T t)..以T為參數地方賦值,但在return T的時候就不知道他具體類型

總結

? extends T是子類型通配符,限定了T的上限,T只能是定義的類型或者他的子類。不可以給T賦值,可以return T成定義的類型

? super T是超類通配符,限定了T的下限,T只能是定義的類型或者他的父類。可以給T賦值為定義的類或者其子類,return T不知道他的具體類型

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。
禁止轉載,如需轉載請通過簡信或評論聯(lián)系作者。

相關閱讀更多精彩內容

  • 泛型 泛型(Generic Type)簡介 通常情況的類和函數,我們只需要使用具體的類型即可:要么是基本類型,要么...
    Tenderness4閱讀 1,511評論 4 2
  • 參考地址:《Java 泛型,你了解類型擦除嗎?》 《Java中的逆變與協(xié)變》 《java 泛型中 T、E .....
    琦小蝦閱讀 3,147評論 0 11
  • Java泛型食用筆記(四) -- 通配符 1. 三種通配符 通配符為一個泛型類所指定的類型集合提供了一個有用的類型...
    不智魚閱讀 1,576評論 0 1
  • Java為什么引入泛型 眾所周知,Java 5才最大的亮點就是引入泛型,那么Java引入泛型的目的是什么?這就需要...
    大棋17閱讀 1,914評論 0 3
  • 以后我的出現(xiàn)要做什么準備? 提前一天看稿,對鏡子練習一遍,自己操練一遍。提前邀請學員。提前布置場地。提前準備好穿的...
    牟紅梅閱讀 407評論 0 0

友情鏈接更多精彩內容