繼續 泛型的 extends
的內容,如果B
是A
的子類別,如果Node<A>
視為一種Node<B>
,則稱Node
具有逆變性(Contravariance)。也就是說,如果以下程式碼片段沒有發生錯誤,則Node
具有逆變性:
Node<Fruit> fruit = new Node<>(new Fruit(), null);
Node<Banana> node = fruit; // 實際上會編譯錯誤
Java泛型並不支援逆變性,所以實際上第二行會發生編譯錯誤。可以使用型態通配字元?
與super
來宣告,以達到類似逆變性的效果,例如:
Node<Fruit> fruit = new Node<>(new Fruit(), null);
Node<? super Banana> node = fruit;
Node<? super Apple> node = fruit;
這樣的語法支援看似奇怪,可以從實際範例來瞭解如何支援逆變性。假設你想設計了一個籃子,可以指定籃中置放的物品,放置的物品會是同一種類(例如都是一種Fruit
),並有一個排序方法,可指定java.util.Comparator
針對籃中物品進行排序,那麼請問以下泛型未填寫部份該如何宣告?
public class Basket<T> {
public T[] things;
public Basket(T... things) {
this.things = things;
}
public void sort(Comparator<__________> comparator) {
// 作一些排序
}
}
宣告為<? extends T>
嗎?如果是這樣的話,籃中裝Apple
時,在呼叫sort()
方法時,就必須傳入實作Comparator<Apple>
的物件。例如針對price
比較時:
Basket<Apple> apples = new Basket<>(
new Apple(20, 100), new Apple(25, 150));
// 以下使用 JDK8 Lambda 可寫為
// apples.sort((apple1, apple2) -> apple1.price - apple2.price);
apples.sort(new Comparator<Apple>() {
@Override
public int compare(Apple apple1, Apple apple2) {
return apple1.price – apple2.price;
}
});
因為宣告為Basket<Apple>
,sort()
方法就只接受Comparator<? extends Apple>
的物件。同樣地,籃中裝Banana
時,在呼叫sort()
方法時,就必須傳入實作Comparator<Banana>
的物件。例如:
Basket<Banana> bananas = new Basket<>(
new Banana(30, 200), new Banana(50, 300));
// 以下使用 JDK8 Lambda 可寫為
// bananas.sort((banana1, banana2) -> banana1.price - banana2.price);
bananas.sort(new Comparator<Banana>() {
@Override
public int compare(Banana banana1, Banana banana2) {
return banana1.price - banana2.price;
}
});
然而就以上兩段程式範例來看,Apple
或Banana
都是針對price
比較,也就是都是針對水果價格比較,也就是都是針對Fruit
繼承而來的price
比較,並不需要個別指定Comparator<Apple>
與Comparator<Banana>
兩個比較器,你希望可以有以下的操作:
// 以下使用 JDK8 Lambda 可寫為
// Comparator<Fruit> priceComparato = (fruit1, fruit2) -> fruit1.price - fruit2.price;
Comparator<Fruit> priceComparator = new Comparator<Fruit>() {
@Override
public int compare(Fruit fruit1, Fruit fruit2) {
return fruit1.price – fruit2.price;
}
};
Basket<Apple> apples = new Basket<>(
new Apple(20, 100), new Apple(25, 150));
apples.sort(priceComparator);
Basket<Banana> bananas = new Basket<>(
new Banana(30, 200), new Banana(50, 300));
bananas.sort(priceComparator);
如果是如此,那麼sort()
方法的Comparator
泛型應該宣告為<? super T>
。例如:
package cc.openhome;
import java.util.Comparator;
public class Basket<T> {
public T[] things;
public Basket(T... things) {
this.things = things;
}
public void sort(Comparator<? super T> comparator) {
Arrays.sort(things, comparator);
}
}
範例中使用了java.util.Arrays
的sort()
方法進行排序,你可以查看API文件,可以發現sort()
方法的第二個參數,正是宣告為Comparator<? super T>
:
可以用以下範例看看是否能正確排序:
package cc.openhome;
import java.util.*;
public class ContravarianceDemo {
public static void main(String[] args) {
// 以下使用 JDK8 Lambda 可寫為
// Comparator<Fruit> byPrice = (fruit1, fruit2) -> fruit1.price - fruit2.price;
Comparator<Fruit> byPrice = new Comparator<Fruit>() {
@Override
public int compare(Fruit fruit1, Fruit fruit2) {
return fruit1.price - fruit2.price;
}
};
Basket<Apple> apples = new Basket<>(
new Apple(25, 150), new Apple(20, 100));
apples.sort(byPrice);
printlnForEach(apples);
Basket<Banana> bananas = new Basket<>(
new Banana(50, 300), new Banana(30, 200));
bananas.sort(byPrice);
printlnForEach(bananas);
}
private static void printlnForEach(Basket<? extends Fruit> basket) {
for(Fruit fruit : basket.things) {
System.out.printf("%s(%d, %d) ", fruit.getClass().getName(),
fruit.price, fruit.weight);
}
System.out.println();
}
}
執行結果如下:
cc.openhome.Banana(30, 200) cc.openhome.Banana(50, 300)
如果泛型類別或介面不具共變性或逆變性,則稱為不可變的(Nonvariant)或嚴謹的(Rigid),Java屬於此類。