服务热线:13616026886

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

位置:首页 > 技术文档 > JAVA > 核心技术 > 查看文档

java 范型攻略篇

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

版权声明:techtarget获matrix授权发布,如需转载请联系matrix
作者:管斌 blog:(http://blog.matrix.org.cn/page/guanbing)
原文地址:http://www.matrix.org.cn/resource/article/44/44344_java+generics.html

扫描关注微信公众号