总结Java的Generics

总结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的Generics)