協(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不知道他的具體類型