如果你定義了以下的類別:
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>
。
沒有留言:
張貼留言
你好!歡迎你在我的 Blog 上留下你寶貴的意見。