在已發(fā)布的Java1.4中在核心代碼庫中增加了許多新的API(如Loging,正則表達式,NIO)等,在最新發(fā)布的JDK1.5和即將發(fā)布的JDK1.6中也新增了許多API,其中比較有重大意義的就是Generics(范型)。
一.什么是Generics?
Generics可以稱之為參數(shù)類型(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。我們看第二個方法,就出現(xiàn)了一個問題,Collection在內(nèi)部用的是Object,而我們要取出Collection中的對象時,需要進行Cast,那么開發(fā)者必需用實際的類型進行Cast,像這種向下造型,編譯器無
法進行檢查,如此一來我們就要冒在代碼在運行拋出ClassCastException的危險。我們看inspecCollection方法,編譯時沒有問題,但在運行時就會拋出ClassCastException異常。所以我們一定要遠離這個重大的運行時錯誤
二.使用Generics 從上一章節(jié)中的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行我們在創(chuàng)建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,無論是方法的參數(shù)還是返回值。這時我們在看Example?2中的第四行,編譯就會反映出編譯錯誤。 所以在java中增加Generics主要的目的是為了增加類型安全。
通過上面的簡單的例子我們看到使用Generics的好處有: 1.在類型沒有變化時,Collection是類型安全的。 2.內(nèi)在的類型轉(zhuǎn)換優(yōu)于在外部的人工造型。 3.使Java?接口更加強壯,因為它增加了類型。 4.類型的匹配錯誤在編譯階段就可以捕捉到,而不是在代碼運行時。
受約束類型變量 雖然許多Class被設計成Generics,但類型變量可以是受限的 public?class?C1<T?extends?Number>?{?} public?class?C2<T?extends?Person?&?Comparable>?{?}? 第一個T變量必須繼承Number,第二個T必須繼承Person和實現(xiàn)Comparable
三.Generics?方法
像Generics類一樣,方法和構(gòu)造函數(shù)也可以有類型參數(shù)。方法的參數(shù)的返回值都可以有類型參數(shù),進行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方法的幾個約束。 //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,注意返回值沒有進行造型。 在Example?5第2行參數(shù)都為String,所以返回值也是String,注意返回值沒有進行造型。以上都調(diào)用了同一個方法。 在Example?5第3行產(chǎn)生以下編譯錯誤: 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");
這個錯誤發(fā)生是因為編譯器無法確定返回值類型,因為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,此時編譯器不允許這樣的事出現(xiàn),所以第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>(下界)?
六.創(chuàng)建自己的范型 以下代碼來自http://www.java2s.com/ExampleCode/Language-Basics 1.一個參數(shù)的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.二個參數(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(); ????????} ????} }??
|
|