龙盟编程博客 | 无障碍搜索 | 云盘搜索神器
快速搜索
主页 > 软件开发 > JAVA开发 >

Java 范型攻略篇

时间:2009-12-23 15:42来源:未知 作者:admin 点击:
分享到:
在已发布的Java1.4中在核心代码库中增加了许多新的API(如Loging,正则表达式,NIO)等,在最新发布的JDK1.5和即将发布的JDK1.6中也新增了许多API,其中比较有重大意义的就是Generics(范型)。

     在已发布的Java1.4中在核心代码库中增加了许多新的API(如Loging,正则表达式,NIO)等,在最新发布的JDK1.5和即将发布的JDK1.6中也新增了许多API,其中比较有重大意义的就是Generics(范型)。
  
  一.什么是Generics?
  
  Generics可以称之为参数类型(parameterized types),由编译器来验证从客户端将一种类型传送给某一对象的机制。如Java.util.ArrayList,
  
  编译器可以用Generics来保证类型安全。
  在我们深入了解Generics之前,我们先来看一看当前的java 集合框架(Collection)。在j2SE1.4中所有集合的Root Interface是Collection
  
  Collections example without genericity: Example 1
  
  
  
1 protected void collectionsExample() {
  2  ArrayList list = new ArrayList();
  3  list.add(new String("test string"));
  4  list.add(new Integer(9)); // purposely placed here to create a runtime ClassCastException
  5  inspectCollection(list);
  6 }
  7
  8
  9 protected void inspectCollection(Collection aCollection) {
  10  Iterator i = aCollection.iterator();
  11  while (i.hasNext()) {
  12   String element = (String) i.next();
  13  }
  14 }

  
  以上的样例程序包含的两个方法,collectionExample方法建立了一个简单的集合类型ArrayList,并在ArrayList中增加了一个String和一个Integer对象.而在inspecCollection方法中,我们迭代这个ArrayList用String进行Cast。我们看第二个方法,就出现了一个问题,Collection在内部用的是Object,而我们要取出Collection中的对象时,需要进行Cast,那么开发者必需用实际的类型进行Cast,像这种向下造型,编译器无
  
  法进行检查,如此一来我们就要冒在代码在运行抛出ClassCastException的危险。我们看inspecCollection方法,编译时没有问题,但在运行时就会抛出ClassCastException异常。所以我们一定要远离这个重大的运行时错误
  
  
  二.使用Generics
  从上一章节中的CassCastException这种异常,我们期望在代码编译时就能够捕捉到,下面我们使用范型修改上一章的样例程序。
  //Example 2
  
1 protected void collectionsExample() {
  2  ArrayList<String> list = new ArrayList<String>();
  3  list.add(new String("test string"));
  4  // list.add(new Integer(9)); this no longer compiles
  5  inspectCollection(list);
  6 }
  7
  8
  9 protected void inspectCollection(Collection<String> aCollection) {
  10  Iterator<String> i = aCollection.iterator();
  11  while(i.hasNext()) {
  12   String element = i.next();
  13  }
  14 }

  
  从上面第2行我们在创建ArrayList时使用了新语法,在JDK1.5中所有的Collection都加入了Generics的声明。例:
  //Example 3
  
1 public class ArrayList<E> extends AbstractList<E> {
  2  // details omitted...
  3  public void add(E element) {
  4   // details omitted
  5  }
  6  public Iterator<E> iterator() {
  7   // details omitted
  8  }
  9 }

  
  这个E是一个类型变量,并没有对它进行具体类型的定义,它只是在定义ArrayList时的类型占位符,在Example 2中的我们在定义ArrayList的实
  
  例时用String绑定在E上,当我们用add(E element)方法向ArrayList中增加对象时, 那么就像下面的写法一样: public void add(String element);因为在ArrayList所有方法都会用String来替代E,无论是方法的参数还是返回值。这时我们在看Example 2中的第四行,编译就会反映出编译错误。
  所以在java中增加Generics主要的目的是为了增加类型安全。
  
  通过上面的简单的例子我们看到使用Generics的好处有:
  1.在类型没有变化时,Collection是类型安全的。
  2.内在的类型转换优于在外部的人工造型。
  3.使Java 接口更加强壮,因为它增加了类型。
  4.类型的匹配错误在编译阶段就可以捕捉到,而不是在代码运行时。
  
  受约束类型变量
  虽然许多Class被设计成Generics,但类型变量可以是受限的
  public class C1<T extends Number> { }
  public class C2<T extends Person & Comparable> { }
  第一个T变量必须继续Number,第二个T必须继续Person和实现Comparable
  
  三.Generics 方法
  
  像Generics类一样,方法和构造函数也可以有类型参数。方法的参数的返回值都可以有类型参数,进行Generics。
  //Example 4
  
1 public <T extends Comparable> T max(T t1, T t2) {
  2  if (t1.compareTo(t2) > 0)
  3   return t1;
  4  else return t2;
  5 }

  
  这里,max方法的参数类型为单一的T类型,而T类型继续了Comparable,max的参数和返回值都有相同的超类。下面的Example 5显示了max方法的几个约束。
  //Example 5 
  
1 Integer iresult = max(new Integer(100), new Integer(200));
  2 String sresult = max("AA", "BB");
  3 Number nresult = max(new Integer(100), "AAA"); // does not compile

  
  在Example 5第1行参数都为Integer,所以返回值也是Integer,注重返回值没有进行造型。
  在Example 5第2行参数都为String,所以返回值也是String,注重返回值没有进行造型。以上都调用了同一个方法。
  在Example 5第3行产生以下编译错误:
  Example.java:10: incompatible types
  found  : java.lang.Object&java.io.Serializable&java.lang.Comparable<?>
  required: java.lang.Number
      Number nresult = max(new Integer(100), "AAA");
  
  这个错误发生是因为编译器无法确定返回值类型,因为String和Integer都有相同的超类Object,注重就算我们修正了第三行,这行代码在运行仍然会报错,因为比较了不同的对象。
  
  四.向下兼容
  任何一个新的特色在新的JDK版本中出来后,我们首先关心的是如何于以前编写的代码兼容。也就是说我们编写的Example 1程序不需要任何的改变就可以运行,但是编译器会给出一个"ROW TYPE"的警告。在JDK1.4中编写的代码如何在JVM1.5中完全兼容运行,我们要人工进行一个:Type erasure处理过程
  
  五.通配符
  
  //Example 6
  
List<String> stringList = new ArrayList<String>(); //1
  List<Object> objectList = stringList ;//2
  objectList .add(new Object()); // 3
  String s = stringList .get(0);//4

  
  乍一看,Example
  
  6是正确的。但stringList本意是存放String类型的ArrayList,而objectList中可以存入任何对象,当在第3行进行处理时,stringList也就无法保证是String类型的ArrayList,此时编译器不答应这样的事出现,所以第3行将无法编译。
  
  //Example 7
  
void printCollection(Collection<Object> c) 
  { for (Object e : c) {
  System.out.println(e);
  }}

  
  Example 7的本意是打印所有Collection的对象,但是正如Example 6所说的,编译会报错,此时就可以用通配符“?”来修改Example 7
  
  //Example 8
  
void printCollection(Collection<?> c) 
  { for (Object e : c) {
  System.out.println(e);
  }}

  
  Example 8中所有Collection类型就可以方便的打印了
  
  有界通配符 <T extends Number>(上界) <T super Number>(下界)
  
  六.创建自己的范型
  以下代码来自http://www.java2s.com/ExampleCode/Language-Basics
  1.一个参数的Generics
  //Example 9(没有使用范型)
  
class NonGen {  
    Object ob; // ob is now of type Object
    // Pass the constrUCtor a reference to  
    // an object of type Object
    NonGen(Object o) {  
      ob = o;  
    }  
    // Return type Object.
    Object getob() {  
      return ob;  
    }  
    // Show type of ob.  
    void showType() {  
      System.out.println("Type of ob is " +  
                         ob.getClass().getName());  
    }  
  }  
  // Demonstrate the non-generic class.  
  public class NonGenDemo {  
    public static void main(String args[]) {  
      NonGen iOb;  
      // Create NonGen Object and store
      // an Integer in it. Autoboxing still occurs.
      iOb = new NonGen(88);  
      // Show the type of data used by iOb.
      iOb.showType();
      // Get the value of iOb.
      // This time, a cast is necessary.
      int v = (Integer) iOb.getob();  
      System.out.println("value: " + v);  
      System.out.println();  
      // Create another NonGen object and  
      // store a String in it.
      NonGen strOb = new NonGen("Non-Generics Test");  
      // Show the type of data used by strOb.
      strOb.showType();
      // Get the value of strOb.
      // Again, notice that a cast is necessary.  
      String str = (String) strOb.getob();  
      System.out.println("value: " + str);  
      // This compiles, but is conceptually wrong!
      iOb = strOb;
      v = (Integer) iOb.getob(); // runtime error!
    }  
  }
  
  
  //Example 10(使用范型)
  
class Example1<T>{
   private T t;
   Example1(T o){
    this.t=o;
    }
   T getOb(){
    return t;
   }
   void ShowObject(){
    System.out.println("对象的类型是:"+t.getClass().getName());
   }
  }
  public class GenericsExample1 {
  
   /**
    * @param args
    */
   public static void main(String[] args) {
    // TODO Auto-generated method stub
    Example1<Integer> examplei=new Example1<Integer>(100);
    examplei.ShowObject();
    System.out.println("对象是:"+examplei.getOb());
    Example1<String> examples=new Example1<String>("Bill");
    examples.ShowObject();
    System.out.println("对象是:"+examples.getOb());
   }
  
  }

  
  我们来看Example 9没有使用范型,所以我们需要进行造型,而Example 10我们不需要任何的造型
  
  2.二个参数的Generics
  
  //Example 11
  
class TwoGen<T, V> { 
     T ob1;
     V ob2;
     // Pass the constructor a reference to  
     // an object of type T.
     TwoGen(T o1, V o2) {
       ob1 = o1;
       ob2 = o2;
     }
     // Show types of T and V.
     void showTypes() {
       System.out.println("Type of T is " +
                          ob1.getClass().getName());
       System.out.println("Type of V is " +
                          ob2.getClass().getName());
     }
     T getob1() {
       return ob1;
     }
     V getob2() {
       return ob2;
     }
   }
  
  public class GenericsExampleByTwoParam {
  
   /**
    * @param args
    */
   public static void main(String[] args) {
    // TODO Auto-generated method stub
    TwoGen<Integer, String> tgObj =
         new TwoGen<Integer, String>(88, "Generics");
       // Show the types.
       tgObj.showTypes();
       // OBTain and show values.
       int v = tgObj.getob1();
       System.out.println("value: " + v);
       String str = tgObj.getob2();
       System.out.println("value: " + str);
     }
  
   }

  
  3.Generics的Hierarchy
  
  //Example 12
  
class Stats<T extends Number> {  
     T[] nums; // array of Number or subclass
     // Pass the constructor a reference to  
     // an array of type Number or subclass.
     Stats(T[] o) {  
       nums = o;  
     }  
     // Return type double in all cases.
     double average() {  
       double sum = 0.0;
       for(int i=0; i < nums.length; i++)  
         sum += nums[i].doubleValue();
       return sum / nums.length;
     }  
   }  
  public class GenericsExampleByHierarchy {
  
  
   /**
    * @param args
    */
   public static void main(String[] args) {
    // TODO Auto-generated method stub
  
     Integer inums[] = { 1, 2, 3, 4, 5 };
       Stats<Integer> iob = new Stats<Integer>(inums);  
       double v = iob.average();
       System.out.println("iob average is " + v);
       Double dnums[] = { 1.1, 2.2, 3.3, 4.4, 5.5 };
       Stats<Double> dob = new Stats<Double>(dnums);  
       double w = dob.average();
       System.out.println("dob average is " + w);
       // This won't compile because String is not a
       // subclass of Number.
  //     String strs[] = { "1", "2", "3", "4", "5" };
  //     Stats<String> strob = new Stats<String>(strs);  
  //     double x = strob.average();
  //     System.out.println("strob average is " + v);
     }  
   }
  
  
  4.使用通配符
  //Example 14
  
class StatsWildCard<T extends Number> {
   T[] nums; // array of Number or subclass
   // Pass the constructor a reference to
   // an array of type Number or subclass.
   StatsWildCard(T[] o) {
    nums = o;
   }
   // Return type double in all cases.
   double average() {
    double sum = 0.0;
    for (int i = 0; i < nums.length; i++)
     sum += nums[i].doubleValue();
    return sum / nums.length;
   }
   // Determine if two averages are the same.
   // Notice the use of the wildcard.
   boolean sameAvg(StatsWildCard<?> ob) {
    if (average() == ob.average())
     return true;
    return false;
   }
  }
  
  public class GenericsExampleByWildcard {
  
   /**
    * @param args
    */
   public static void main(String[] args) {
    // TODO Auto-generated method stub
    Integer inums[] = { 1, 2, 3, 4, 5 };
    StatsWildCard<Integer> iob = new StatsWildCard<Integer>(inums);
    double v = iob.average();
    System.out.println("iob average is " + v);
    Double dnums[] = { 1.1, 2.2, 3.3, 4.4, 5.5 };
    StatsWildCard<Double> dob = new StatsWildCard<Double>(dnums);
    double w = dob.average();
    System.out.println("dob average is " + w);
    Float fnums[] = { 1.0F, 2.0F, 3.0F, 4.0F, 5.0F };
    StatsWildCard<Float> fob = new StatsWildCard<Float>(fnums);
    double x = fob.average();
    System.out.println("fob average is " + x);
    // See which arrays have same average.
    System.out.print("Averages of iob and dob ");
    if (iob.sameAvg(dob))
     System.out.println("are the same.");
    else
     System.out.println("differ.");
    System.out.print("Averages of iob and fob ");
    if (iob.sameAvg(fob))
     System.out.println("are the same.");
    else
     System.out.println("differ.");
  
   }
  
  }

  
  5.使用边界通配符
  //Example 15
  
class TwoD { 
    int x, y;
    TwoD(int a, int b) {
      x = a;
      y = b;
    }
  }
  // Three-dimensional coordinates.
  class ThreeD extends TwoD {
    int z;
    ThreeD(int a, int b, int c) {
      super(a, b);
      z = c;
    }
  }
  // Four-dimensional coordinates.
  class FourD extends ThreeD {
    int t;
    FourD(int a, int b, int c, int d) {
      super(a, b, c);
      t = d;  
    }
  }
  // This class holds an array of coordinate objects.
  class Coords<T extends TwoD> {
    T[] coords;
    Coords(T[] o) { coords = o; }
  }
  // Demonstrate a bounded wildcard.
  public class BoundedWildcard {
    static void showXY(Coords<?> c) {
      System.out.println("X Y Coordinates:");
      for(int i=0; i < c.coords.length; i++)
        System.out.println(c.coords[i].x + " " +
                           c.coords[i].y);
      System.out.println();
    }
    static void showXYZ(Coords<? extends ThreeD> c) {
      System.out.println("X Y Z Coordinates:");
      for(int i=0; i < c.coords.length; i++)
        System.out.println(c.coords[i].x + " " +
                           c.coords[i].y + " " +
                           c.coords[i].z);
      System.out.println();
    }
    static void showAll(Coords<? extends FourD> c) {
      System.out.println("X Y Z T Coordinates:");
      for(int i=0; i < c.coords.length; i++)
        System.out.println(c.coords[i].x + " " +
                           c.coords[i].y + " " +
                           c.coords[i].z + " " +
                           c.coords[i].t);
      System.out.println();
    }
    public static void main(String args[]) {
      TwoD td[] = {
        new TwoD(0, 0),
        new TwoD(7, 9),
        new TwoD(18, 4),
        new TwoD(-1, -23)
      };
      Coords<TwoD> tdlocs = new Coords<TwoD>(td);    
      System.out.println("Contents of tdlocs.");
      showXY(tdlocs); // OK, is a TwoD
  //  showXYZ(tdlocs); // Error, not a ThreeD
  //  showAll(tdlocs); // Erorr, not a FourD
      // Now, create some FourD objects.
      FourD fd[] = {
        new FourD(1, 2, 3, 4),
        new FourD(6, 8, 14, 8),
        new FourD(22, 9, 4, 9),
        new FourD(3, -2, -23, 17)
      };
      Coords<FourD> fdlocs = new Coords<FourD>(fd);    
      System.out.println("Contents of fdlocs.");
      // These are all OK.
      showXY(fdlocs);  
      showXYZ(fdlocs);
      showAll(fdlocs);
    }
  }

  
  
  6.ArrayList的Generics
  //Example 16
  
public class ArrayListGenericDemo {
    public static void main(String[] args) {
      ArrayList<String> data = new ArrayList<String>();
      data.add("hello");
      data.add("goodbye");
  
      // data.add(new Date()); This won't compile!
  
      Iterator<String> it = data.iterator();
      while (it.hasNext()) {
        String s = it.next();
        System.out.println(s);
      }
    }
  }

  
  7.HashMap的Generics
  //Example 17
  
public class HashDemoGeneric {
    public static void main(String[] args) {
      HashMap<Integer,String> map = new HashMap<Integer,String>();
  
      map.put(1, "Ian");
      map.put(42, "Scott");
      map.put(123, "Somebody else");
  
      String name = map.get(42);
      System.out.println(name);
    }
  }

  
  8.接口的Generics
  //Example 18
  
interface MinMax<T extends Comparable<T>> { 
    T min();
    T max();
  }
  // Now, implement MinMax
  class MyClass<T extends Comparable<T>> implements MinMax<T> {
    T[] vals;
    MyClass(T[] o) { vals = o; }
    // Return the minimum value in vals.
    public T min() {
      T v = vals[0];
      for(int i=1; i < vals.length; i++)
        if(vals[i].compareTo(v) < 0) v = vals[i];
      return v;
    }
    // Return the maximum value in vals.
    public T max() {
      T v = vals[0];
      for(int i=1; i < vals.length; i++)
        if(vals[i].compareTo(v) > 0) v = vals[i];
      return v;
    }
  }
  public class GenIFDemo {
    public static void main(String args[]) {
      Integer inums[] = {3, 6, 2, 8, 6 };
      Character chs[] = {'b', 'r', 'p', 'w' };
      MyClass<Integer> iob = new MyClass<Integer>(inums);
      MyClass<Character> cob = new MyClass<Character>(chs);
      System.out.println("Max value in inums: " + iob.max());
      System.out.println("Min value in inums: " + iob.min());
      System.out.println("Max value in chs: " + cob.max());
      System.out.println("Min value in chs: " + cob.min());
    }
  }

  
  9.Exception的Generics
  //Example 20
  
interface Executor<E extends Exception> {
      void execute() throws E;
  }
  
  public class GenericExceptionTest {
      public static void main(String args[]) {
          try {
              Executor<IOException> e =
                  new Executor<IOException>() {
                  public void execute() throws IOException
                  {
                      // code here that may throw an
                      // IOException or a subtype of
                      // IOException
                  }
              };
  
              e.execute();
          } catch(IOException ioe) {
              System.out.println("IOException: " + ioe);
              ioe.printStackTrace();
          }
      }
  }  

  
精彩图集

赞助商链接