作者:管斌(蘇州星動(dòng)) blog:(http://blog./page/guanbing)
在已發(fā)布的Java1.4中在核心代碼庫(kù)中增加了許多新的API(如Loging,正則表達(dá)式,NIO)等,在最新發(fā)布的JDK1.5和即將發(fā)布的JDK1.6中也新增了許多API,其中比較有重大意義的就是Generics(范型)。
一.什么是Generics?
Generics可以稱之為參數(shù)類型(parameterized types),由編譯器來(lái)驗(yàn)證從客戶端將一種類型傳送給某一對(duì)象的機(jī)制。如Java.util.ArrayList,
編譯器可以用Generics來(lái)保證類型安全。 在我們深入了解Generics之前,我們先來(lái)看一看當(dāng)前的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 }
以上的樣例程序包含的兩個(gè)方法,collectionExample方法建立了一個(gè)簡(jiǎn)單的集合類型ArrayList,并在ArrayList中增加了一個(gè)String和一個(gè)Integer對(duì)象.而在inspecCollection方法中,我們迭代這個(gè)ArrayList用String進(jìn)行Cast。我們看第二個(gè)方法,就出現(xiàn)了一個(gè)問(wèn)題,Collection在內(nèi)部用的是Object,而我們要取出Collection中的對(duì)象時(shí),需要進(jìn)行Cast,那么開(kāi)發(fā)者必需用實(shí)際的類型進(jìn)行Cast,像這種向下造型,編譯器無(wú)
法進(jìn)行檢查,如此一來(lái)我們就要冒在代碼在運(yùn)行拋出ClassCastException的危險(xiǎn)。我們看inspecCollection方法,編譯時(shí)沒(méi)有問(wèn)題,但在運(yùn)行時(shí)就會(huì)拋出ClassCastException異常。所以我們一定要遠(yuǎn)離這個(gè)重大的運(yùn)行時(shí)錯(cuò)誤
二.使用Generics 從上一章節(jié)中的CassCastException這種異常,我們期望在代碼編譯時(shí)就能夠捕捉到,下面我們使用范型修改上一章的樣例程序。 //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行我們?cè)趧?chuàng)建ArrayList時(shí)使用了新語(yǔ)法,在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 }
這個(gè)E是一個(gè)類型變量,并沒(méi)有對(duì)它進(jìn)行具體類型的定義,它只是在定義ArrayList時(shí)的類型占位符,在Example 2中的我們?cè)诙xArrayList的實(shí)
例時(shí)用String綁定在E上,當(dāng)我們用add(E element)方法向ArrayList中增加對(duì)象時(shí), 那么就像下面的寫法一樣: public void add(String element);因?yàn)樵贏rrayList所有方法都會(huì)用String來(lái)替代E,無(wú)論是方法的參數(shù)還是返回值。這時(shí)我們?cè)诳碋xample 2中的第四行,編譯就會(huì)反映出編譯錯(cuò)誤。 所以在java中增加Generics主要的目的是為了增加類型安全。
通過(guò)上面的簡(jiǎn)單的例子我們看到使用Generics的好處有: 1.在類型沒(méi)有變化時(shí),Collection是類型安全的。 2.內(nèi)在的類型轉(zhuǎn)換優(yōu)于在外部的人工造型。 3.使Java 接口更加強(qiáng)壯,因?yàn)樗黾恿祟愋汀?br>4.類型的匹配錯(cuò)誤在編譯階段就可以捕捉到,而不是在代碼運(yùn)行時(shí)。
受約束類型變量 雖然許多Class被設(shè)計(jì)成Generics,但類型變量可以是受限的 public class C1<T extends Number> { } public class C2<T extends Person & Comparable> { } 第一個(gè)T變量必須繼承Number,第二個(gè)T必須繼承Person和實(shí)現(xiàn)Comparable
三.Generics 方法
像Generics類一樣,方法和構(gòu)造函數(shù)也可以有類型參數(shù)。方法的參數(shù)的返回值都可以有類型參數(shù),進(jìn)行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方法的參數(shù)類型為單一的T類型,而T類型繼承了Comparable,max的參數(shù)和返回值都有相同的超類。下面的Example 5顯示了max方法的幾個(gè)約束。 //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行參數(shù)都為Integer,所以返回值也是Integer,注意返回值沒(méi)有進(jìn)行造型。 在Example 5第2行參數(shù)都為String,所以返回值也是String,注意返回值沒(méi)有進(jìn)行造型。以上都調(diào)用了同一個(gè)方法。 在Example 5第3行產(chǎn)生以下編譯錯(cuò)誤: 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");
這個(gè)錯(cuò)誤發(fā)生是因?yàn)榫幾g器無(wú)法確定返回值類型,因?yàn)镾tring和Integer都有相同的超類Object,注意就算我們修正了第三行,這行代碼在運(yùn)行仍然會(huì)報(bào)錯(cuò),因?yàn)楸容^了不同的對(duì)象。
四.向下兼容 任何一個(gè)新的特色在新的JDK版本中出來(lái)后,我們首先關(guān)心的是如何于以前編寫的代碼兼容。也就是說(shuō)我們編寫的Example 1程序不需要任何的改變就可以運(yùn)行,但是編譯器會(huì)給出一個(gè)"ROW TYPE"的警告。在JDK1.4中編寫的代碼如何在JVM1.5中完全兼容運(yùn)行,我們要人工進(jìn)行一個(gè):Type erasure處理過(guò)程
五.通配符
//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中可以存入任何對(duì)象,當(dāng)在第3行進(jìn)行處理時(shí),stringList也就無(wú)法保證是String類型的ArrayList,此時(shí)編譯器不允許這樣的事出現(xiàn),所以第3行將無(wú)法編譯。
//Example 7
void printCollection(Collection<Object> c) { for (Object e : c) { System.out.println(e); }}
Example 7的本意是打印所有Collection的對(duì)象,但是正如Example 6所說(shuō)的,編譯會(huì)報(bào)錯(cuò),此時(shí)就可以用通配符“?”來(lái)修改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>(下界)
六.創(chuàng)建自己的范型 以下代碼來(lái)自http://www./ExampleCode/Language-Basics 1.一個(gè)參數(shù)的Generics //Example 9(沒(méi)有使用范型)
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("對(duì)象的類型是:"+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("對(duì)象是:"+examplei.getOb()); Example1<String> examples=new Example1<String>("Bill"); examples.ShowObject(); System.out.println("對(duì)象是:"+examples.getOb()); }
}
我們來(lái)看Example 9沒(méi)有使用范型,所以我們需要進(jìn)行造型,而Example 10我們不需要任何的造型
2.二個(gè)參數(shù)的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(); } } }
|