我只收這種東西


如果你定義了以下的類別:
class Node<T> {
T value;
Node<T> next;

Node(T value, Node<T> next) {
this.value = value;
this.next = next;
}
}

如果在以下的例子中:
class Fruit {}
class Apple extends Fruit {
@Override
public String toString() {
return "Apple";
}
}

class Banana extends Fruit {
@Override
public String toString() {
return "Banana";
}
}


public class Main {
public static void main(String[] args) {
Node<Apple> apple = new Node<Apple>(new Apple(), null);
Node<Fruit> fruit = apple; // 編譯錯誤,incompatible types
  }
}

在 範例中,apple的型態是Node<Apple>,而fruit的型態為Node<Fruit>,你將apple所參考的物件 給fruit參考,那麼Node<Apple>該是一種Node<Fruit>呢?在上例中編譯器給你的答案為「不是」!

如 果B是A的子型態,而Node<B>被視為一種Node<A>型態,則稱Node具有共變性(Covariance)或有彈性的(flexible)。如 果Node<A>被視為一種Node<B>型態,則稱Node具有逆變性(Contravariance)。如果不具共變性或逆變性,則Node是不可變 的(nonvariant)或嚴謹的(rigid)。

Java的泛型不支援共變性,不過可以使用型態通配字元?與extends來宣告變數,使其達到類似共變性,例如:
public class Main {
public static void main(String[] args) {
Node<Apple> apple = new Node<Apple>(new Apple(), null);
Node<? extends Fruit> fruit = apple; // 類似共變性效果
}
}

一個實際應用的例子是:
public class Main {
public static void main(String[] args) {
Node<Apple> apple1 = new Node<Apple>(new Apple(), null);
Node<Apple> apple2 = new Node<Apple>(new Apple(), apple1);
Node<Apple> apple3 = new Node<Apple>(new Apple(), apple2);

Node<Banana> banana1 = new Node<Banana>(new Banana(), null);
Node<Banana> banana2 = new Node<Banana>(new Banana(), banana1);

show(apple3);
show(banana2);
}

static void show(Node<? extends Fruit> n) {
Node<? extends Fruit> node = n;
do {
System.out.println(node.value);
node = node.next;
} while(node != null);
}
}

你的目的是可以顯示所有的水果節點,由於show()方法使用型態通配字元宣告參數,使得n具備類似共變性的效果,因此show()方法就可以顯示Node<Apple>也可以顯示Node<Banana>。

Java的泛型亦不支援逆變性,不過可以使用型態通配字元?與super來宣告變數,使其達到類似逆變性,例如:
public class Main {
public static void main(String[] args) {
Node<Fruit> fruit = new Node<Fruit>(new Fruit(), null);
Node<? super Apple> apple = fruit;
Node<? super Banana> banana = fruit;
}
}

一個實際應用的例子如下:
class Fruit {
int price;
int weight;
Fruit(int price, int weight) {
this.price = price;
this.weight = weight;
}
}

class Apple extends Fruit {
Apple(int price, int weight) {
super(price, weight);
}
}

class Banana extends Fruit {
Banana(int price, int weight) {
super(price, weight);
}
}

interface Comparator<T> {
int compare(T t1, T t2);
}

class Basket<T> {
private T[] things;
Basket(T... things) {
this.things = things;
}
void sort(Comparator<? super T> comparator) {
// 作一些排序
}
}

籃子(Basket)中可以置放各種物品,並可以傳入一個比較器(Comparator)進行排序。假設你分別在兩個籃子中放置了蘋果(Apple)與香蕉(Banana):
public class Main {
public static void main(String[] args) {
Comparator<Fruit> comparator = new Comparator<Fruit>() {
public int compare(Fruit f1, Fruit f2) {
return f1.price - f2.price;
}
};
Basket<Apple> b1 = new Basket<Apple>(
new Apple(20, 100), new Apple(25, 150));
Basket<Banana> b2 = new Basket<Banana>(
new Banana(30, 200), new Banana(25, 250));
b1.sort(comparator);
b2.sort(comparator);
}
}

現在b1的型態為Basket<Apple>,而b2的型態為Basket<Banana>,你可以如上實作一個水果(Fruit)比較器,比較水果的價格進行排序,這可以同時適用於Basket<Apple>與Basket<Banana>。