您好,登錄后才能下訂單哦!
小編給大家分享一下Java中泛型機制有什么用,相信大部分人都還不怎么了解,因此分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后大有收獲,下面讓我們一起去了解一下吧!
Java 在 5.0 時增加了泛型機制,據說專家們為此花費了 5 年左右的時間(聽起來很不容易)。有了泛型之后,尤其是對集合類的使用,就變得更規范了。
看下面這段簡單的代碼。
ArrayList<String>list=newArrayList<String>();list.add("沉默王二");Stringstr=list.get(0);但在沒有泛型之前該怎么辦呢?
首先,我們需要使用 Object 數組來設計 Arraylist 類。
classArraylist{privateObject[]objs;privateinti=0;publicvoidadd(Objectobj){objs[i++]=obj;}publicObjectget(inti){returnobjs[i];}}然后,我們向 Arraylist 中存取數據。
Arraylistlist=newArraylist();list.add("沉默王二");list.add(newDate());Stringstr=(String)list.get(0);你有沒有發現兩個問題:
Arraylist 可以存放任何類型的數據(既可以存字符串,也可以混入日期),因為所有類都繼承自 Object 類。
從 Arraylist 取出數據的時候需要強制類型轉換,因為編譯器并不能確定你取的是字符串還是日期。
對比一下,你就能明顯地感受到泛型的優秀之處:使用類型參數解決了元素的不確定性——參數類型為 String 的集合中是不允許存放其他類型元素的,取出數據的時候也不需要強制類型轉換了。
二哥,怎么設計泛型啊?
三妹啊,你一個小白只要會用泛型就行了,還想設計泛型啊?!不過,既然你想了解,那么哥義不容辭。
首先,我們來按照泛型的標準重新設計一下 Arraylist 類。
classArraylist<E>{privateObject[]elementData;privateintsize=0;publicArraylist(intinitialCapacity){this.elementData=newObject[initialCapacity];}publicbooleanadd(Ee){elementData[size++]=e;returntrue;}EelementData(intindex){return(E)elementData[index];}}
一個泛型類就是具有一個或多個類型變量的類。Arraylist 類引入的類型變量為 E(Element,元素的首字母),使用尖括號 <> 括起來,放在類名的后面。
然后,我們可以用具體的類型(比如字符串)替換類型變量來實例化泛型類。
Arraylist<String>list=newArraylist<String>();list.add("沉默王三");Stringstr=list.get(0);
Date 類型也可以的。
Arraylist<Date>list=newArraylist<Date>();list.add(newDate());Datedate=list.get(0);
其次,我們還可以在一個非泛型的類(或者泛型類)中定義泛型方法。
classArraylist<E>{public<T>T[]toArray(T[]a){return(T[])Arrays.copyOf(elementData,size,a.getClass());}}
不過,說實話,泛型方法的定義看起來略顯晦澀。來一副圖吧(注意:方法返回類型和方法參數類型至少需要一個)。
現在,我們來調用一下泛型方法。
Arraylist<String>list=newArraylist<>(4);list.add("沉");list.add("默");list.add("王");list.add("二");String[]strs=newString[4];strs=list.toArray(strs);for(Stringstr:strs){System.out.println(str);}
最后,我們再來說說泛型變量的限定符 extends。在解釋這個限定符之前,我們假設有三個類,它們之間的定義是這樣的。
classWanglaoer{publicStringtoString(){return"王老二";}}classWangerextendsWanglaoer{publicStringtoString(){return"王二";}}classWangxiaoerextendsWanger{publicStringtoString(){return"王小二";}}
我們使用限定符 extends 來重新設計一下 Arraylist 類。
classArraylist<EextendsWanger>{}
當我們向 Arraylist 中添加 Wanglaoer 元素的時候,編譯器會提示錯誤:Arraylist 只允許添加 Wanger 及其子類 Wangxiaoer 對象,不允許添加其父類 Wanglaoer。
Arraylist<Wanger>list=newArraylist<>(3);list.add(newWanger());list.add(newWanglaoer());//Themethodadd(Wanger)inthetypeArraylist<Wanger>isnotapplicableforthearguments//(Wanglaoer)list.add(newWangxiaoer());
也就是說,限定符 extends 可以縮小泛型的類型范圍。
二哥,聽說虛擬機沒有泛型?
三妹,你功課做得可以啊,連虛擬機都知道了啊。哥可以肯定地回答你,虛擬機是沒有泛型的。
啰嗦一句哈。我們編寫的 Java 代碼(也就是源碼,后綴為 .java 的文件)是不能夠被操作系統直接識別的,需要先編譯,生成 .class 文件(也就是字節碼文件)。然后 Java 虛擬機(JVM)會充當一個翻譯官的角色,把字節碼翻譯給操作系統能聽得懂的語言,告訴它該干嘛。
怎么確定虛擬機沒有泛型呢?我們需要把泛型類的字節碼進行反編譯——強烈推薦超神反編譯工具 Jad !
現在,在命令行中敲以下代碼吧(反編譯 Arraylist 的字節碼文件 Arraylist.class)。
jadArraylist.class
命令執行完后,會生成一個 Arraylist.jad 的文件,用文本編輯工具打開后的結果如下。
//DecompiledbyJadv1.5.8g.Copyright2001PavelKouznetsov.//Jadhomepage:http://www.kpdus.com/jad.html//Decompileroptions:packimports(3)//SourceFileName:Arraylist.javapackagecom.cmower.java_demo.fanxing;importjava.util.Arrays;classArraylist{publicArraylist(intinitialCapacity){size=0;elementData=newObject[initialCapacity];}publicbooleanadd(Objecte){elementData[size++]=e;returntrue;}ObjectelementData(intindex){returnelementData[index];}privateObjectelementData[];privateintsize;}
類型變量 <E> 消失了,取而代之的是 Object !
既然如此,那如果泛型類使用了限定符 extends,結果會怎么樣呢?我們先來看看 Arraylist2 的源碼。
classArraylist2<EextendsWanger>{privateObject[]elementData;privateintsize=0;publicArraylist2(intinitialCapacity){this.elementData=newObject[initialCapacity];}publicbooleanadd(Ee){elementData[size++]=e;returntrue;}EelementData(intindex){return(E)elementData[index];}}字節碼文件Arraylist2.class使用Jad反編譯后的結果如下。//DecompiledbyJadv1.5.8g.Copyright2001PavelKouznetsov.//Jadhomepage:http://www.kpdus.com/jad.html//Decompileroptions:packimports(3)//SourceFileName:Arraylist2.javapackagecom.cmower.java_demo.fanxing;//Referencedclassesofpackagecom.cmower.java_demo.fanxing://WangerclassArraylist2{publicArraylist2(intinitialCapacity){size=0;elementData=newObject[initialCapacity];}publicbooleanadd(Wangere){elementData[size++]=e;returntrue;}WangerelementData(intindex){return(Wanger)elementData[index];}privateObjectelementData[];privateintsize;}
類型變量 <E extends Wanger> 不見了,E 被替換成了 Wanger。
通過以上兩個例子說明,Java 虛擬機會將泛型的類型變量擦除,并替換為限定類型(沒有限定的話,就用 Object)。
二哥,類型擦除會有什么問題嗎?
三妹啊,你還別說,類型擦除真的會有一些“問題”。
我們來看一下這段代碼。
publicclassCmower{publicstaticvoidmethod(Arraylist<String>list){System.out.println("Arraylist<String>list");}publicstaticvoidmethod(Arraylist<Date>list){System.out.println("Arraylist<Date>list");}}
在淺層的意識上,我們會想當然地認為 Arraylist<String> list 和 Arraylist<Date> list 是兩種不同的類型,因為 String 和 Date 是不同的類。
但由于類型擦除的原因,以上代碼是不會通過編譯的——編譯器會提示一個錯誤(這正是類型擦除引發的那些“問題”):
Erasure of method method(Arraylist) is the same as another method in type
CmowerErasure of method method(Arraylist) is the same as another method in type
Cmower
大致的意思就是,這兩個方法的參數類型在擦除后是相同的。
也就是說,method(Arraylist<String> list) 和 method(Arraylist<Date> list) 是同一種參數類型的方法,不能同時存在。類型變量 String 和 Date 在擦除后會自動消失,method 方法的實際參數是 Arraylist list。
有句俗話叫做:“百聞不如一見”,但即使見到了也未必為真——泛型的擦除問題就可以很好地佐證這個觀點。
二哥,聽說泛型還有通配符?
三妹啊,哥突然覺得你很適合作一枚可愛的程序媛啊!你這預習的功課做得可真到家啊,連通配符都知道!
通配符使用英文的問號(?)來表示。在我們創建一個泛型對象時,可以使用關鍵字 extends 限定子類,也可以使用關鍵字 super 限定父類。
為了更好地解釋通配符,我們需要對 Arraylist 進行一些改進。
classArraylist<E>{privateObject[]elementData;privateintsize=0;publicArraylist(intinitialCapacity){this.elementData=newObject[initialCapacity];}publicbooleanadd(Ee){elementData[size++]=e;returntrue;}publicEget(intindex){return(E)elementData[index];}publicintindexOf(Objecto){if(o==null){for(inti=0;i<size;i++)if(elementData[i]==null)returni;}else{for(inti=0;i<size;i++)if(o.equals(elementData[i]))returni;}return-1;}publicbooleancontains(Objecto){returnindexOf(o)>=0;}publicStringtoString(){StringBuildersb=newStringBuilder();for(Objecto:elementData){if(o!=null){Ee=(E)o;sb.append(e.toString());sb.append(',').append('');}}returnsb.toString();}publicintsize(){returnsize;}publicEset(intindex,Eelement){EoldValue=(E)elementData[index];elementData[index]=element;returnoldValue;}}
1)新增 indexOf(Object o) 方法,判斷元素在 Arraylist 中的位置。注意參數為 Object 而不是泛型 E。
2)新增 contains(Object o) 方法,判斷元素是否在 Arraylist 中。注意參數為 Object 而不是泛型 E。
3)新增 toString() 方法,方便對 Arraylist 進行打印。
4)新增 set(int index, E element) 方法,方便對 Arraylist 元素的更改。
你知道,Arraylist<Wanger> list = new Arraylist<Wangxiaoer>(); 這樣的語句是無法通過編譯的,盡管 Wangxiaoer 是 Wanger 的子類。但如果我們確實需要這種 “向上轉型” 的關系,該怎么辦呢?這時候就需要通配符來發揮作用了。
利用 <? extends Wanger> 形式的通配符,可以實現泛型的向上轉型,來看例子。
Arraylist<?extendsWanger>list2=newArraylist<>(4);list2.add(null);//list2.add(newWanger());//list2.add(newWangxiaoer());Wangerw2=list2.get(0);//Wangxiaoerw3=list2.get(1);
list2 的類型是 Arraylist<? extends Wanger>,翻譯一下就是,list2 是一個 Arraylist,其類型是 Wanger 及其子類。
注意,“關鍵”來了!list2 并不允許通過 add(E e) 方法向其添加 Wanger 或者 Wangxiaoer 的對象,唯一例外的是 null。為什么不能存呢?原因還有待探究(苦澀)。
那就奇了怪了,既然不讓存放元素,那要 Arraylist<? extends Wanger> 這樣的 list2 有什么用呢?
雖然不能通過 add(E e) 方法往 list2 中添加元素,但可以給它賦值。
Arraylist<Wanger>list=newArraylist<>(4);Wangerwanger=newWanger();list.add(wanger);Wangxiaoerwangxiaoer=newWangxiaoer();list.add(wangxiaoer);Arraylist<?extendsWanger>list2=list;Wangerw2=list2.get(1);System.out.println(w2);System.out.println(list2.indexOf(wanger));System.out.println(list2.contains(newWangxiaoer()));
Arraylist<? extends Wanger> list2 = list; 語句把 list 的值賦予了 list2,此時 list2 == list。由于 list2 不允許往其添加其他元素,所以此時它是安全的——我們可以從容地對 list2 進行 get()、indexOf() 和 contains()。想一想,如果可以向 list2 添加元素的話,這 3 個方法反而變得不太安全,它們的值可能就會變。
利用 <? super Wanger> 形式的通配符,可以向 Arraylist 中存入父類是 Wanger 的元素,來看例子。
Arraylist<?superWanger>list3=newArraylist<>(4);list3.add(newWanger());list3.add(newWangxiaoer());//Wangerw3=list3.get(0);
需要注意的是,無法從 Arraylist<? super Wanger> 這樣類型的 list3 中取出數據。為什么不能取呢?原因還有待探究(再次苦澀)。
雖然原因有待探究,但結論是明確的:<? extends T> 可以取數據,<? super T> 可以存數據。那么利用這一點,我們就可以實現數組的拷貝——<? extends T> 作為源(保證源不會發生變化),<? super T> 作為目標(可以保存值)。
publicclassCollections{publicstatic<T>voidcopy(Arraylist<?superT>dest,Arraylist<?extendsT>src){for(inti=0;i<src.size();i++)dest.set(i,src.get(i));}}
以上是“Java中泛型機制有什么用”這篇文章的所有內容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內容對大家有所幫助,如果還想學習更多知識,歡迎關注億速云行業資訊頻道!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。