java 中協(xié)變,逆變,不變簡單理解
1. 什么是協(xié)變、逆變、不變
假設有兩個類,Dog和Animal,如果用Dog <= Animal 表示它倆的繼承關系。用f(type) 表示類型構(gòu)造器,一個已知的類型被類型構(gòu)造器處理后就是一個嶄新的類型。
協(xié)變就是f(Dog)是f(Animal)的子類,即f(Dog) <= f(Animal);逆變就是f(Animal)是f(Dog)的子類,即f(Aniaml) <= f(Dog);不變就是指f(Dog)與f(Animal)之間沒有關系
類 型構(gòu)造器可以是泛型
List<Animal>, 可以是數(shù)組Animal[],可以是函數(shù)方法method(Animal)
2. java 泛型和數(shù)組
-
? java泛型不支持逆變和協(xié)變,只能是不變
List<Animal> animals = new ArrayList<Dog>(); // 編譯錯誤,java泛型不支持逆變和協(xié)變,只能是不變
-
? java數(shù)組支持協(xié)變,不支持逆變,但也正因為支持協(xié)變,數(shù)組可能踩坑
Animal[] animals = new Dog[10];
animals[0] = new Dog();
animals[1] = new Cat(); // 運行時異常 java.lang.ArrayStoreException
3. java 泛型協(xié)變&上界
-
? 考慮泛型支持協(xié)變后有什么好處,解決了什么問題?
// processAnimals方法中如果不支持泛型協(xié)變,那么難道要通過接收類型的不同重寫好多的方法嗎?太麻煩了!!!
public static void main(String[] args) {
processAnimals(new ArrayList<Cat>()); // 編譯錯誤
proceessAimals(new ArrayList<Dog>()); // 編譯錯誤
}
public static void processAnimals(List<Animal> animals) {
// ...
}
-
? 在java泛型中加入extends關鍵字實現(xiàn)支持協(xié)變,
<? extends Animal>其中?代表不確定類型通配符,和extends結(jié)合就聲明了泛型的上限,表示接收的類型只能是指定類型或是該類型的子類 -
? 上面說數(shù)組支持協(xié)變,添加其他類型會出現(xiàn)運行時異常,泛型協(xié)變?yōu)榱硕沤^這種隱患,所以泛型協(xié)變除了null 可以寫,其他的都不能寫(編譯異常),可以讀
-
? 指定上界的好處,限定類型(編譯錯誤提醒);可以訪問上界類型中的方法(要不只能訪問Object類中方法)
// 使用extends關鍵字讓泛型支持協(xié)變,這樣processAniamls方法中的泛型變量就能接收子類集合了
public static void processAnimals(List<> extends Animal> anumals){
}
public static void processAnimalsExtends(List<? extends Animal> animals) {
animals.add(null); // 正常
/**
* 協(xié)變不允許傳入除null
*/
// animals.add(new Animal()); // 編譯錯誤
// animals.add(new Dog()); // 編譯錯誤
animals.remove(new Dog()); // 不會破壞類型安全
animals.contains(new Dog()); // 不會破壞類型安全
}
public class AKA<T extends Animal>{
public static void main(String[] args) {
AKA<Animal> animalAKA = new AKA<>();
AKA<Cat> catAKA = new AKA<>();
AKA<String> stringAKA = new AKA<String>();// 編譯錯誤
}
}
4.java 泛型逆變&下界
-
? 使用super關鍵字聲明泛型下界,如
<? super Dog>,逆變后就可以接收本類型或父類型的泛型類 -
? 逆變可以添加元素,逆變泛型可以接受本類型及父類型元素,但是添加元素只能添加指定類型或指定類型的子類
public static void testDog(List<? super Dog> dest,list<? extends Dog> src){
for(Dog dog :src){
if(dog.isHappy()){
dest.add(dog);
}
}
}
public static void main(Stringp[] args){
testDog(new ArrayList<Animal>(),new ArrayList<哈士奇>()); // 正確
}
5.應用場景
-
? 只讀不寫:用協(xié)變
-
? 只寫不讀:用逆變
-
? 又讀又寫:用不變
