effective java 摘選條目分享 2 - 泛型
TRANSCRIPT
2015/03/31 讀書會分享
Effective Java摘選條目分享 2
- 泛型
Kane
大綱
泛型 Generic緣由
特性
泛型方法
Bounded Wildcard Type & PECS顯式、隱藏
異構
沒有泛型的日子
List list = new ArrayList();list.add(“string”);list.add(new Foo());
String item1 = (String) list.get(0);Foo item2 = (Foo) list.get(1);
依賴於程式設計師之間的約定
容易出錯
泛型來了
List<String> list = new ArrayList<String>();list.add(“string1”);list.add(new Foo()); // compile error
String item1 = list.get(0);Foo item2 = list.get(1); // compile error
由 compiler 保證類型安全 (type-safe)
特質
compile time 時消除類型資訊
(E[]) list.toArray(); // warning: unchecked type
不可變 invariant
List<Number> list = ...
list.add(new Integer(10)); // compile error
泛型方法
public static <T> boolean isNull(T[] array) { return (array == null);}
isNull(new String[1]);isNull(new Foo[2]);
T: typeE: collectionK, V: map
?: wildcard (any)
Example for Bounded Wildcard TypeExample:
class Stack { public Stack(); public void push(E e); public E pop(); public boolean isEmpty();}
想要新增 pushAll() 及 popAll()
pushAll() impl.public void pushAll(Iterable<E> src) { for (E e : src) push(e);}
放不進去
public void pushAll(Iterable<E> src) { for (E e : src) push(e);}
Stack<Number> stack = …Iterable<Integer> integers = …stack.pushAll(integers); // compile error
用 extends 限制參數類型
public void pushAll(Iterable<E> src) { for (E e : src) push(e);}
public void pushAll(Iterable<? extends E> src)
Stack<Number> stack = …Iterable<Integer> integers = …stack.pushAll(integers); // compile error
popAll() impl.public void popAll(Collection<E> dst) { while (!isEmpty()) dst.add(pop());}
還是放不進去
public void popAll(Collection<E> dst) { while (!isEmpty()) dst.add(pop());}
Stack<Number> stack = …Collection<Object> objects = …stack.popAll(objects); // compile error
用 super 限制參數類型
public void popAll(Collection<E> dst) { while (!isEmpty()) dst.add(pop());}
public void pushAll(Collection<? super E> dst)
Stack<Number> stack = …Collection<Object> objects = …stack.popAll(objects); // compile error
規則:PECS
producer-extends
public void pushAll(Iterable<? extends E> src)
consumer-super
public void popAll(Collection<? super E> dst)
src 提供元素:生產者
dst 獲取元素:消費者
PECS:幫你解決轉換的困擾
public static <T extends Comparable<T>> T max(List<T> list) {
public static <T extends Comparable<? super T>> T max(List<? extends T> list)
max() 裡面用到:
1. Iterator<T> i = list.iterator();
2. t.compareTo(result)
list 提供元素,是生產者
t 獲取元素進行比較,是消費者
顯式的類型參數
public static <E> Set<E> union(Set<? extends E> s1, Set<? extends E> s2)
Set<Integer> integers = …Set<Double> doubles = …Set<Number> numbers = union(integers, doubles); // compile error
Set<Number> numbers = Union.<Number>union(integers, doubles);
static method, 所以放 class name
隱藏類型參數 1/3public static <E> void swap(List<E> list, int i, int j)
public static void swap(List<?> list, int i, int j)
Which one is better?
隱藏類型參數 2/3public static void swap(List<?> list, int i, int j) { list.set(i, list.set(j, list.get(i))); // compile error}
List<?>
compiler 完全不知道裡面放什麼類型
隱藏類型參數 3/3public static void swap(List<?> list, int i, int j) { swapHelper(list, i, j);}
// for wildcard captureprivate static <E> void swapHelper(List<E> list, int i, int j) { list.set(i, list.set(j, list.get(i)));}
異構容器 (heterogeneous container)Type Token 概念,以 type 為 key 存取值
public class Favorite { public <T> void putFavorite(Class<T> type, T instance); public <T> T getFavorite(Class<T> type);}
fav.put(String.class, “XD”);fav.put(Integer.class, 20);
fav.get(String.class); “XD”
實作範例 - 利用動態 castpublic class Favorite {
private Map<Class<?>, Object> favorites = new HashMap<Class<?>, Object>();
public <T> void putFavorite(Class<T> type, T instance) {
// check type null
favorites.put(type, type.cast(instance));
}
public <T> T getFavorite(Class<T> type) {
return type.cast(favorites.get(type));
}
}
若 client 誤用,可拋出ClassCastException
拿出來是 Object 類型,但回傳值要求 T 類型
Q & A