总结Java的Generics(一)

2014-11-24 09:09:53 · 作者: · 浏览: 3

从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{}

public interface Generator {}

也可以应用于方法,比如:

public void f(T x) { }

使用泛型时,需要时时牢记的是,运行时的泛型代码中不真的保留泛型参数类型(There’s no information about generic parameter types available inside generic code. )所以运行时,List和List是一回事。

注意泛型不总是实用,比如:

1: class Manipulator2 {
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 {
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 {
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 {
2: Class kind;
3: public ClassTypeCapture(Class 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 { }

注意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 flist = new ArrayList();
4: }
如果非要upcast的话可以这样写:

List< extends Fruit> flist = new ArrayList();

需要注意的是,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: }