Java中泛型機制有什么用-創(chuàng)新互聯(lián)

小編給大家分享一下Java中泛型機制有什么用,相信大部分人都還不怎么了解,因此分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后大有收獲,下面讓我們一起去了解一下吧!

成都地區(qū)優(yōu)秀IDC服務器托管提供商(創(chuàng)新互聯(lián)公司).為客戶提供專業(yè)的西部信息中心,四川各地服務器托管,西部信息中心、多線服務器托管.托管咨詢專線:18982081108

Java 在 5.0 時增加了泛型機制,據(jù)說專家們?yōu)榇嘶ㄙM了 5 年左右的時間(聽起來很不容易)。有了泛型之后,尤其是對集合類的使用,就變得更規(guī)范了。

看下面這段簡單的代碼。

ArrayList<String>list=newArrayList<String>();list.add("沉默王二");Stringstr=list.get(0);但在沒有泛型之前該怎么辦呢?

首先,我們需要使用 Object 數(shù)組來設計 Arraylist 類。

classArraylist{privateObject[]objs;privateinti=0;publicvoidadd(Objectobj){objs[i++]=obj;}publicObjectget(inti){returnobjs[i];}}然后,我們向 Arraylist 中存取數(shù)據(jù)。
Arraylistlist=newArraylist();list.add("沉默王二");list.add(newDate());Stringstr=(String)list.get(0);你有沒有發(fā)現(xiàn)兩個問題:
  • Arraylist 可以存放任何類型的數(shù)據(jù)(既可以存字符串,也可以混入日期),因為所有類都繼承自 Object 類。

  • 從 Arraylist 取出數(shù)據(jù)的時候需要強制類型轉(zhuǎn)換,因為編譯器并不能確定你取的是字符串還是日期。

對比一下,你就能明顯地感受到泛型的優(yōu)秀之處:使用類型參數(shù)解決了元素的不確定性——參數(shù)類型為 String 的集合中是不允許存放其他類型元素的,取出數(shù)據(jù)的時候也不需要強制類型轉(zhuǎn)換了。

二哥,怎么設計泛型???

三妹啊,你一個小白只要會用泛型就行了,還想設計泛型?。?!不過,既然你想了解,那么哥義不容辭。

首先,我們來按照泛型的標準重新設計一下 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());}}

不過,說實話,泛型方法的定義看起來略顯晦澀。來一副圖吧(注意:方法返回類型和方法參數(shù)類型至少需要一個)。

Java中泛型機制有什么用

現(xiàn)在,我們來調(diào)用一下泛型方法。

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 的文件)是不能夠被操作系統(tǒng)直接識別的,需要先編譯,生成 .class 文件(也就是字節(jié)碼文件)。然后 Java 虛擬機(JVM)會充當一個翻譯官的角色,把字節(jié)碼翻譯給操作系統(tǒng)能聽得懂的語言,告訴它該干嘛。

怎么確定虛擬機沒有泛型呢?我們需要把泛型類的字節(jié)碼進行反編譯——強烈推薦超神反編譯工具 Jad !

現(xiàn)在,在命令行中敲以下代碼吧(反編譯 Arraylist 的字節(jié)碼文件 Arraylist.class)。

jadArraylist.class

命令執(zhí)行完后,會生成一個 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];}}字節(jié)碼文件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 是不同的類。

但由于類型擦除的原因,以上代碼是不會通過編譯的——編譯器會提示一個錯誤(這正是類型擦除引發(fā)的那些“問題”):

Erasure of method method(Arraylist) is the same as another method in type
Cmower

Erasure of method method(Arraylist) is the same as another method in type
Cmower

大致的意思就是,這兩個方法的參數(shù)類型在擦除后是相同的。

也就是說,method(Arraylist<String> list) 和 method(Arraylist<Date> list) 是同一種參數(shù)類型的方法,不能同時存在。類型變量 String 和 Date 在擦除后會自動消失,method 方法的實際參數(shù)是 Arraylist list。

有句俗話叫做:“百聞不如一見”,但即使見到了也未必為真——泛型的擦除問題就可以很好地佐證這個觀點。

二哥,聽說泛型還有通配符?

三妹啊,哥突然覺得你很適合作一枚可愛的程序媛??!你這預習的功課做得可真到家啊,連通配符都知道!

通配符使用英文的問號(?)來表示。在我們創(chuàng)建一個泛型對象時,可以使用關鍵字 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 中的位置。注意參數(shù)為 Object 而不是泛型 E。

2)新增 contains(Object o) 方法,判斷元素是否在 Arraylist 中。注意參數(shù)為 Object 而不是泛型 E。

3)新增 toString() 方法,方便對 Arraylist 進行打印。

4)新增 set(int index, E element) 方法,方便對 Arraylist 元素的更改。

你知道,Arraylist<Wanger> list = new Arraylist<Wangxiaoer>(); 這樣的語句是無法通過編譯的,盡管 Wangxiaoer 是 Wanger 的子類。但如果我們確實需要這種 “向上轉(zhuǎn)型” 的關系,該怎么辦呢?這時候就需要通配符來發(fā)揮作用了。

利用 <? extends Wanger> 形式的通配符,可以實現(xiàn)泛型的向上轉(zhuǎn)型,來看例子。

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 中取出數(shù)據(jù)。為什么不能取呢?原因還有待探究(再次苦澀)。

雖然原因有待探究,但結論是明確的:<? extends T> 可以取數(shù)據(jù),<? super T> 可以存數(shù)據(jù)。那么利用這一點,我們就可以實現(xiàn)數(shù)組的拷貝——<? extends T> 作為源(保證源不會發(fā)生變化),<? 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中泛型機制有什么用”這篇文章的所有內(nèi)容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內(nèi)容對大家有所幫助,如果還想學習更多知識,歡迎關注創(chuàng)新互聯(lián)-成都網(wǎng)站建設公司行業(yè)資訊頻道!

本文標題:Java中泛型機制有什么用-創(chuàng)新互聯(lián)
瀏覽路徑:http://bm7419.com/article0/giiio.html

成都網(wǎng)站建設公司_創(chuàng)新互聯(lián),為您提供關鍵詞優(yōu)化網(wǎng)站策劃、Google商城網(wǎng)站、動態(tài)網(wǎng)站、云服務器

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)

外貿(mào)網(wǎng)站制作