服务热线:13616026886

技术文档 欢迎使用技术文档,我们为你提供从新手到专业开发者的所有资源,你也可以通过它日益精进

位置:首页 > 技术文档 > JAVA > 新手入门 > 基础入门 > 查看文档

java 范型攻略篇


  在已发布的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();
        }
    }
}  

扫描关注微信公众号