协变逆变java_Java中的逆变与协变
什么是逆變與協變
協變(Covariance)
如果B是A的子類,并且F(B)也是F(A)的子類,那么F即為協變
逆變(Contravariance)
如果B是A的子類,并且F(B)成了F(A)的父類,那么F即為逆變
Java中的逆變與協變
Java中的泛型有逆變和協變兩種操作,定義如下:
協變
extends A>
B是A的子類,那么List< B >是List extends A>的子類
逆變
supper A>
B是A的子類,那么List< B >是List super A>的父類
Java中逆變與協變的約束
Java的協變逆變及其約束,都是出于對多態的應用。為了后續說明方便,這里先定義一系列的父子類
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";
}
}
多態
Java是支持多態的。如果一個方法的參數接收的是A類型,那么將其子類型作為參數,調用該方法,依然可行。
例如eatFruitMeat方法就能體現多態特性
@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());
}
協變約束
協變方法支持對傳入參數的讀操作,但不支持修改操作。如下:
@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方法在被調用前,并不知道最終調用方,傳遞進來的具體是哪一個子類?有可能是List< Apple >,也有可能是List< GreenApple >,所以貿然向其中添加任何對象,都是可能出錯,比如你不能把一個Apple對象放進List< GreenApple >。為了防止這些可能的錯誤,編譯器提前進行了約束限制。
逆變約束
逆變主要在寫的場景,即只能向逆變容器中添加,下界類型本身或其子類
@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作為參數調用collectFruits方法,因為不滿足 Java 方法參數的多態性要求,即只能傳本類或子類的要求
編譯錯誤2: 如果調用方傳遞的是List< Apple >,那往其中添加父類Fruit對象,在運行時肯定會報錯,為了避免這種情況,編譯器提前報錯。
總結
Java泛型支持協變和逆變,具體在使用時,會有一些約束。這些約束,需要從Java語言的特性,比如多態性,以及運行時安全性去理解。
簡單總結協變、逆變參數的方法調用特點如下:
協變參數
只接受自己的子類。協變的父子關系,同類原本的父子關系一致。如GreenApple是Apple的子類,List< GreenApple >是List extends Apple>的子類
對寫有約束,只能用于讀
逆變參數
只接受自己的子類。逆變的父子關系,同類原本父子關系相反。如GreenApple是Apple的子類,List< GreenApple >是List super Apple>的父類
只能寫入下界的子類,本例中,只能向List中寫入Apple及Apple的子類
參考鏈接
總結
以上是生活随笔為你收集整理的协变逆变java_Java中的逆变与协变的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: java 时间轮算法_时间轮算法(Tim
- 下一篇: java list子类_List集合的子