必要性
  在程序日益复杂庞大的,编写泛用性代码的价值愈发变得巨大。
  而要做到这一点,其诀窍仅只两字而已—— 解耦 。
  简单的解耦,无疑是使用基类替代子类。然而由于Java仅支持单继承,这种解耦方法所带来的局限性未免过大,有种“只准投胎一次”的感觉。
  使用接口替代具体类算是更进了一步,算是多给了一条命吧,但限制仍旧存在。要是我们所写的代码本身是为了应用于“某种不确定的类型”呢?
  这时候轮到 泛型 登场了。
  简单泛型
  虽然理想远大。但Java引入泛型的初衷,也许只是为了创造 容器类 也说不定。
  站在类库设计者的角度,我们不妨走上一遭。
  得益于单根继承结构,我们可以这样来设计一个持有单个对象的容器:
  public class Holder1 {
  private Object a;
  public Holder1(Object a) {
  this.a = a;
  }
  Object get() {
  return a;
  }
  }
  这个容器确实能持有多种类型的对象,但通常而言我们只会用它来存储一种对象。也是说虽然设计时希望能存储 任意类型 ,但使用时却能够只存储我们想要的 确定类型 。
  泛型可以达到这一目的,与此同时,这也能使编译器为我们提供编译期检查。
  class Automobile {}
  public class Holder2<T> {
  private T a;
  public Holder2(T a) {
  this.a = a;
  }
  public void set(T a) {
  this.a = a;
  }
  public T get() {
  return a;
  }
  public static void main(String[] args) {
  Holder2<Automobile> h2 =
  new Holder2<Automobile>(new Automobile());
  Automobile a = h2.get(); // No cast needed
  // h2.set("Not an Automobile"); // Error
  // h2.set(1); // Error
  }
  }
  如你所见,使用方法即为在类名后添加尖括号,然后填写 类型参数 “T”。使用时用明确的类型参数替换掉“T”,即为该容器指定了其存储的 确定类型 。
  泛型方法
  泛型可以应用于方法,只需要将 泛型参数列表 放在方法返回值之前即可。
  下面这个例子中, f() 的效果看起来像是重载过一样:
  //: generics/GenericMethods.java
  public class GenericMethods {
  public <T> void f(T x) {
  System.out.println(x.getClass().getName());
  }
  public static void main(String[] args) {
  GenericMethods gm = new GenericMethods();
  gm.f("");
  gm.f(1);
  gm.f(1.0);
  gm.f(1.0F);
  gm.f(‘c’);
  gm.f(gm);
  }
  } /* Output:
  java.lang.String
  java.lang.Integer
  java.lang.Double
  java.lang.Float
  java.lang.Character
  GenericMethods
  *///:~
  能这样做的原因在于编译器拥有称为 类型参数推断 的功能,能为我们找出具体的类型。
  注意,如果调用 f() 时传入了基本数据类型, 自动打包机制 将会被触发,将基本数据类型包装为对应的对象。
  擦除
  Java泛型是使用擦除来实现的,这意味着 在泛型代码内部,无法获得关于类型参数的信息 。
  谨记,泛型类型参数将擦除到它的第一个边界,默认边界为Object;对于<T extends Bound>,第一个边界为Bound,即像是在类的声明中使用Bound替换掉T一样。
  以下例子说明了这一问题:
  //: generics/ErasedTypeEquivalence.java
  import java.util.*;
  public class ErasedTypeEquivalence {
  public static void main(String[] args) {
  Class c1 = new ArrayList<String>().getClass();
  Class c2 = new ArrayList<Integer>().getClass();
  System.out.println(c1 == c2);
  }
  } /* Output:
  true
  *///:~
  尽管运行时指定了不同的泛型参数,但”ArrayList<String>”与”ArrayList<Integer>”事实上却被擦除成了相同的原生类型“ArrayList”来进行处理;用类字面常量来进行说明应该会更为直观: “c1”与”c2″的值为“ArrayList.class”,而不是“ArrayList<String>.class”与“ArrayList<Integer>.class” 。
  知道了这一点后,你或许能猜测出容器类的一些具体实现细节了。
  打开ArrayList的源码,会发现在其内部,用来存储数据的数组是这样定义的:
  /**
  * The elements in this list, followed by nulls.
  */
  transient Object[] array;
  而其get()方法则是这样:
  @SuppressWarnings("unchecked") @Override public E get(int index) {
  if (index >= size) {
  throwIndexOutOfBoundsException(index, size);
  }
  return (E) array[index];
  }
  注意,当E的第一个边界为Object时,那么这个方法实际上根本没有进行转型(从Object到Object)。
  知道了这一点后,你大概会对以下代码为何能符合预期地运行感到疑惑:
  //: generics/GenericHolder.java
  public class GenericHolder<T> {
  private T obj;
  public void set(T obj) { this.obj = obj; }
  public T get() { return obj; }
  public static void main(String[] args) {
  GenericHolder<String> holder =
  new GenericHolder<String>();
  holder.set("Item");
  String s = holder.get(); // Why it works?
  }
  } ///:~
  使用 javap -c 反编译,我们可以找到答案:
  public void set(java.lang.Object);
  0: aload_0
  1: aload_1
  2: putfield #2; //Field obj:Object;
  5: return
  public java.lang.Object get();
  0: aload_0
  1: getfield #2; //Field obj:Object;
  4: areturn
  public static void main(java.lang.String[]);
  0: new #3; //class GenericHolder
  3: dup
  4: invokespecial #4; //Method "<init>":()V
  7: astore_1
  8: aload_1
  9: ldc #5; //String Item
  11: invokevirtual #6; //Method set:(Object;)V
  14: aload_1
  15: invokevirtual #7; //Method get:()Object;
  18: checkcast #8; //class java/lang/String --------Watch this line--------
  21: astore_2
  22: return
  奥秘是, 编译器在编译期为我们执行类型检查,然后插入了转型代码。
  再看下面这个例子:
  //: generics/ArrayMaker.java
  import java.lang.reflect.*;
  import java.util.*;
  public class ArrayMaker<T> {
  private Class<T> kind;
  public ArrayMaker(Class<T> kind) { this.kind = kind; }
  @SuppressWarnings("unchecked")
  T[] create(int size) {
  return (T[])Array.newInstance(kind, size);
  }
  public static void main(String[] args) {
  ArrayMaker<String> stringMaker =
  new ArrayMaker<String>(String.class);
  String[] stringArray = stringMaker.create(9);
  System.out.println(Arrays.toString(stringArray));
  }
  } /* Output:
  [null, null, null, null, null, null, null, null, null]
  *///:~
  因为擦除的关系,kind只是被存储为Class,使用“Array.newInstance();”创建数组也只能得到非具体的结果,实际使用中我们需要对其进行向下转型,但是 并没有足够的类型信息用以进行类型检查 ,所以对于编译器报错,只能采用注解“@SuppressWarnings(“unchecked”)”强行将其消去。
  通配符
  有些时候你需要限定条件,使用通配符可以满足这一特性。
  这是指定上界的情况:
  //: generics/GenericsAndCovariance.java
  import java.util.*;
  public class GenericsAndCovariance {
  public static void main(String[] args) {
  // Wildcards allow covariance:
  List<? extends Fruit> flist = new ArrayList<Apple>();
  // Compile Error: can’t add any type of object:
  // flist.add(new Apple());
  // flist.add(new Fruit());
  // flist.add(new Object());
  flist.add(null); // Legal but uninteresting
  // We know that it returns at least Fruit:
  Fruit f = flist.get(0);
  }
  } ///:~
  flist的类型为 List<? extends Fruit> ,读作“任何从Fruit继承而来的类型构成的列表”。但这并不意味着这个List将持有任何类型的Fruit,通配符引用的其实是 明确的类型 ,这个例子中它意味着“某种指定了上界为Fruit的具体类型”。