当前位置:编程学习 > JAVA >>

总结Java的Generics

从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


补充:软件开发 , Java ,
CopyRight © 2012 站长网 编程知识问答 www.zzzyk.com All Rights Reserved
部份技术文章来自网络,