從JDK1.4到JDK5中間經歷了兩年半左右的時間,從JDK5到JDK6經歷了兩年左右的時間,從JDK6到JDK7經歷了4年半多的時間。JDK5,6,7這三個版本,只有JDK5有橫空出世的驚艷,一系列new feature明顯改變了Java程序員日常的coding工作:Generics, Annotation, Autoboxing, for each statement.... 其中Java Generics是一個大的新feature. 相較于C++ templates, Java Generics有諸多限制和陷阱,所以用起來有些不爽, 但越是陷阱多,越有必要好好學學。Thinking in java 第四版除了最后一章GUI外一共900頁,其中Generics這一章就90多頁,這篇博客主要是Thinking in java的Generics這章的筆記。
Java Generics 可以應用于Class和Interface,比如:
public class LinkedStack<T>{}
public interface Generator<T> {}
也可以應用于方法,比如:
public <T> void f(T x) { }
使用泛型時,需要時時牢記的是,運行時的泛型代碼中不真的保留泛型參數類型(There’s no information about generic parameter types available inside generic code. )所以運行時,List<String>和List<Integer>是一回事。
注意泛型不總是實用,比如:
1: class Manipulator2<T extends HasF> {
2: private T obj;
3: public Manipulator2(T x) { obj = x; }
4: public void manipulate() { obj.f(); }
5: }
完全可以替代成:
1: class Manipulator3 {
2: private HasF obj;
3: public Manipulator3(HasF x) { obj = x; }
4: public void manipulate() { obj.f(); }
5: }
但是如果原本的code有一點變化,泛型帶來的好處就體現出來了:
1: class ReturnGenericType<T extends HasF> {
2: private T obj;
3: public ReturnGenericType(T x) { obj = x; }
4: public T get() { return obj; }
5: }
注意這個例子里get()方法返回了特定的類型T。
因為run time時泛型丟掉了真正的類型,所以一些操作是不被允許的:
1: public class Erased<T> {
2: private final int SIZE = 100;
3: public static void f(Object arg) {
4: if(arg instanceof T) {} // Error
5: T var = new T(); // Error
6: T[] array = new T[SIZE]; // Error
7: T[] array = (T)new Object[SIZE]; // Unchecked warning
8: }
9: }
這時候可以顯式地傳入Class object參數,書里稱之為Type Tag
1: public class ClassTypeCapture<T> {
2: Class<T> kind;
3: public ClassTypeCapture(Class<T> kind) {
4: this.kind = kind;
5: }
6:
7: public boolean f(Object arg) {
8: return kind.isInstance(arg);
9: }
10: }
Dimension是Class, HasColor是Interface, 可以有如下的寫法
class ColoredDimension<T extends Dimension & HasColor> { }
注意extends后可以有多項,這與Class的繼承不同,并且,Class要放在Interface前面
注意Array對數據類型檢查是很嚴格的:
1: class Fruit {}
2: class Apple extends Fruit {}
3: class Jonathan extends Apple {}
4: class Orange extends Fruit {}
5:
6: public class CovariantArrays {
7: public static void main(String[] args) {
8: Fruit[] fruit = new Apple[10];
9: fruit[0] = new Apple(); // OK
10: fruit[1] = new Jonathan(); // OK
11: // Runtime type is Apple[], not Fruit[] or Orange[]:
12: try {
13: // Compiler allows you to add Fruit:
14: fruit[0] = new Fruit(); // ArrayStoreException
15: } catch(Exception e) { System.out.println(e); }
16: try {
17: // Compiler allows you to add Oranges:
18: fruit[0] = new Orange(); // ArrayStoreException
19: } catch(Exception e) { System.out.println(e); }
20: }
21: }
注意這一句,Fruit[] fruit = new Apple[10]; 實際類型是Apple,那么fruit里就不能加入Fruit或Orange了.
Container不存在upcast, 別混淆了被包含的元素:
1: public class NonCovariantGenerics {
2: // Compile Error: incompatible types:
3: List<Fruit> flist = new ArrayList<Apple>();
4: }
如果非要upcast的話可以這樣寫:
List<? extends Fruit> flist = new ArrayList<Apple>();
需要注意的是,flist不能再往里加new Apple()或new Fruit()或任何東西了,因為compiler看到List<? extends Fruit>, 就會認為里面可能是Apple,也可能是Orange, 你往里填什么他都不認為一定對。
如果想往List里添加,可以這樣寫:
1: public class SuperTypeWildcards {
2: static void writeTo(List<? super Apple> apples) {
3: apples.add(new Apple());
4: apples.add(new Jonathan());
5: // apples.add(new Fruit()); // Error
6: }
7: }
代碼片段:
1: static <T>
2: T wildSubtype(Holder<? extends T> holder, T arg) {
3: // holder.set(arg); // Error:
4: // set(capture of ? extends T) in
5: // Holder<capture of ? extends T>
6: // cannot be applied to (T)
7: T t = holder.get();
8: return t;
9: }
10: static <T>
11: void wildSupertype(Holder<? super T> holder, T arg) {
12: holder.set(arg);
13: // T t = holder.get(); // Error:
14: // Incompatible types: found Object, required T
15:
16: // OK, but type information has been lost:
17: Object obj = holder.get();
18: }
<? extends T> 和 <? super T>兩者中,前者適合get到特定類型T, 但是不能做set操作。后者相反, 可以set特定類型,但是不能get到特定類型。
Holder, Holder<?>這兩個類不一樣,Holder代表可以包含任何類型,Holder<?>代表可以包含一系列同種類型,但不知道是哪種類型,甚至于你不能往里面加入Object