在已发布的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
闽公网安备 35060202000074号