从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