协变逆变java_Java中的逆变与协变
什么是逆變與協(xié)變
協(xié)變(Covariance)
如果B是A的子類,并且F(B)也是F(A)的子類,那么F即為協(xié)變
逆變(Contravariance)
如果B是A的子類,并且F(B)成了F(A)的父類,那么F即為逆變
Java中的逆變與協(xié)變
Java中的泛型有逆變和協(xié)變兩種操作,定義如下:
協(xié)變
extends A>
B是A的子類,那么List< B >是List extends A>的子類
逆變
supper A>
B是A的子類,那么List< B >是List super A>的父類
Java中逆變與協(xié)變的約束
Java的協(xié)變逆變及其約束,都是出于對多態(tài)的應(yīng)用。為了后續(xù)說明方便,這里先定義一系列的父子類
class Fruit {
public String returnMeat() {
return "generic fruit meat";
}
}
class Apple extends Fruit {
@Override
public String returnMeat() {
return "apple meat";
}
}
class GreenApple extends Apple {
@Override
public String returnMeat() {
return "green apple meat";
}
}
多態(tài)
Java是支持多態(tài)的。如果一個方法的參數(shù)接收的是A類型,那么將其子類型作為參數(shù),調(diào)用該方法,依然可行。
例如eatFruitMeat方法就能體現(xiàn)多態(tài)特性
@Test
public void test1() {
eatFruitMeat(new Fruit());//輸出eat generic fruit meat
eatFruitMeat(new Apple());//輸出eat apple meat
eatFruitMeat(new GreenApple());//輸出eat green apple meat
}
public void eatFruitMeat(Fruit fruit) {
System.out.println("eat "+fruit.returnMeat());
}
協(xié)變約束
協(xié)變方法支持對傳入?yún)?shù)的讀操作,但不支持修改操作。如下:
@Test
public void test1() {
List greenApples = Lists.newArrayList(new GreenApple());
List fruits = Lists.newArrayList(new Fruit());
List apples = Lists.newArrayList(new Apple());
eatFruitMeats(greenApples);
eatFruitMeats(fruits);//編譯錯誤1
eatFruitMeats(apples);
}
public void eatFruitMeats(List extends Apple> fruits) {
fruits.forEach(fruit->System.out.println("eat "+fruit.returnMeat()));
fruits.add(new Apple());//編譯錯誤2
fruits.add(new Fruit());//編譯錯誤3
fruits.add(new Object());//編譯錯誤4
}
編譯錯誤1: eatFruitMeats方法接受的List extends Apple>的子類,顯然List不是其子類
編譯錯誤2,3,4: eatFruitMeats方法在被調(diào)用前,并不知道最終調(diào)用方,傳遞進(jìn)來的具體是哪一個子類?有可能是List< Apple >,也有可能是List< GreenApple >,所以貿(mào)然向其中添加任何對象,都是可能出錯,比如你不能把一個Apple對象放進(jìn)List< GreenApple >。為了防止這些可能的錯誤,編譯器提前進(jìn)行了約束限制。
逆變約束
逆變主要在寫的場景,即只能向逆變?nèi)萜髦刑砑?#xff0c;下界類型本身或其子類
@Test
public void test1() {
List fruits = Lists.newArrayList();
List apples = Lists.newArrayList();
List greenAppleLists = Lists.newArrayList();
collectFruits(fruits);
collectFruits(apples);
collectFruits(greenAppleLists);//編譯錯誤1
}
public void collectFruits(List super Apple> fruits) {
fruits.add(new Fruit());//編譯錯誤2
fruits.add(new Apple());
fruits.add(new GreenApple());
}
編譯錯誤1: 由于是逆變,所以List是List super Apple>的父類。所以不能將greenAppleLists作為參數(shù)調(diào)用collectFruits方法,因為不滿足 Java 方法參數(shù)的多態(tài)性要求,即只能傳本類或子類的要求
編譯錯誤2: 如果調(diào)用方傳遞的是List< Apple >,那往其中添加父類Fruit對象,在運(yùn)行時肯定會報錯,為了避免這種情況,編譯器提前報錯。
總結(jié)
Java泛型支持協(xié)變和逆變,具體在使用時,會有一些約束。這些約束,需要從Java語言的特性,比如多態(tài)性,以及運(yùn)行時安全性去理解。
簡單總結(jié)協(xié)變、逆變參數(shù)的方法調(diào)用特點(diǎn)如下:
協(xié)變參數(shù)
只接受自己的子類。協(xié)變的父子關(guān)系,同類原本的父子關(guān)系一致。如GreenApple是Apple的子類,List< GreenApple >是List extends Apple>的子類
對寫有約束,只能用于讀
逆變參數(shù)
只接受自己的子類。逆變的父子關(guān)系,同類原本父子關(guān)系相反。如GreenApple是Apple的子類,List< GreenApple >是List super Apple>的父類
只能寫入下界的子類,本例中,只能向List中寫入Apple及Apple的子類
參考鏈接
總結(jié)
以上是生活随笔為你收集整理的协变逆变java_Java中的逆变与协变的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: java 时间轮算法_时间轮算法(Tim
- 下一篇: java list子类_List集合的子