久久国产成人av_抖音国产毛片_a片网站免费观看_A片无码播放手机在线观看,色五月在线观看,亚洲精品m在线观看,女人自慰的免费网址,悠悠在线观看精品视频,一级日本片免费的,亚洲精品久,国产精品成人久久久久久久

分享

Java泛型編程最全總結(jié)

 昵稱22369024 2015-04-27

1介紹

Java泛型編程是JDK1.5版本后引入的,。泛型讓編程人員能夠使用類型抽象,通常用于集合里面,。下面是一個(gè)不用泛型例子:

 

Java代碼  收藏代碼
  1. List myIntList=new LinkedList(); //1  
  2. myIntList.add(newInteger(0)); //2  
  3. Integer x=(Integer)myIntList.iterator().next(); //3  
 

注意第3行代碼,,但這是讓人很不爽的一點(diǎn),,因?yàn)槌绦騿T肯定知道自己存儲(chǔ)在List里面的對(duì)象類型是Integer,,但是在返回列表中元素時(shí),還是必須強(qiáng)制轉(zhuǎn)換類型,,這是為什么呢,?原因在于,編譯器只能保證迭代器的next()方法返回的是Object類型的對(duì)象,,為保證Integer變量的類型安全,,所以必須強(qiáng)制轉(zhuǎn)換。

這種轉(zhuǎn)換不僅顯得混亂,,更可能導(dǎo)致類型轉(zhuǎn)換異常ClassCastException,,運(yùn)行時(shí)異常往往讓人難以檢測(cè)到。保證列表中的元素為一個(gè)特定的數(shù)據(jù)類型,,這樣就可以取消類型轉(zhuǎn)換,,減少發(fā)生錯(cuò)誤的機(jī)會(huì), 這也是泛型設(shè)計(jì)的初衷,。下面是一個(gè)使用了泛型的例子:

 

Java代碼  收藏代碼
  1. List<Integer> myIntList=newLinkedList<Integer>(); //1’  
  2. myIntList.add(newInteger(0)); //2’  
  3. Integerx=myIntList.iterator().next(); //3’  
 

       在第1行代碼中指定List中存儲(chǔ)的對(duì)象類型為Integer,,這樣在獲取列表中的對(duì)象時(shí),,不必強(qiáng)制轉(zhuǎn)換類型了。

 

2定義簡(jiǎn)單的泛型

下面是一個(gè)引用自java.util包中的接口List和Iterator的定義,,其中用到了泛型技術(shù),。

 

Java代碼  收藏代碼
  1. public interface List<E> {  
  2. <span style="white-space: pre;">    </span>void add(E x);  
  3. <span style="white-space: pre;">    </span>Iterator<E> iterator();  
  4. }  
  5. public interface Iterator<E> {  
  6. <span style="white-space: pre;">    </span>E next();  
  7. <span style="white-space: pre;">    </span>boolean hasNext();  
  8. }  
 

       這跟原生類型沒有什么區(qū)別,只是在接口后面加入了一個(gè)尖括號(hào),,尖括號(hào)里面是一個(gè)類型參數(shù)(定義時(shí)就是一個(gè)格式化的類型參數(shù),,在調(diào)用時(shí)會(huì)使用一個(gè)具體的類型來替換該類型)。

       也許可以這樣認(rèn)為,,List<Integer>表示List中的類型參數(shù)E會(huì)被替換成Integer,。

 

Java代碼  收藏代碼
  1. public interface IntegerList {  
  2. <span style="white-space: pre;">    </span>void add(Integer x)  
  3. <span style="white-space: pre;">    </span>Iterator<Integer> iterator();  
  4. }  
 

       類型擦除指的是通過類型參數(shù)合并,將泛型類型實(shí)例關(guān)聯(lián)到同一份字節(jié)碼上,。編譯器只為泛型類型生成一份字節(jié)碼,,并將其實(shí)例關(guān)聯(lián)到這份字節(jié)碼上,因此泛型類型中的靜態(tài)變量是所有實(shí)例共享的,。此外,,需要注意的是,一個(gè)static方法,,無法訪問泛型類的類型參數(shù),,因?yàn)轭愡€沒有實(shí)例化,所以,,若static方法需要使用泛型能力,,必須使其成為泛型方法。類型擦除的關(guān)鍵在于從泛型類型中清除類型參數(shù)的相關(guān)信息,,并且再必要的時(shí)候添加類型檢查和類型轉(zhuǎn)換的方法,。在使用泛型時(shí),任何具體的類型都被擦除,,唯一知道的是你在使用一個(gè)對(duì)象,。比如:List<String>List<Integer>在運(yùn)行事實(shí)上是相同的類型。他們都被擦除成他們的原生類型,,即List,。因?yàn)榫幾g的時(shí)候會(huì)有類型擦除,所以不能通過同一個(gè)泛型類的實(shí)例來區(qū)分方法,,如下面的例子編譯時(shí)會(huì)出錯(cuò),,因?yàn)轭愋筒脸螅瑑蓚€(gè)方法都是List類型的參數(shù),,因此并不能根據(jù)泛型類的類型來區(qū)分方法,。

 

Java代碼  收藏代碼
  1. /*會(huì)導(dǎo)致編譯時(shí)錯(cuò)誤*/   
  2.  public class Erasure{  
  3.             public void test(List<String> ls){  
  4.                 System.out.println("Sting");  
  5.             }  
  6.             public void test(List<Integer> li){  
  7.                 System.out.println("Integer");  
  8.             }  
  9.   }  
 

       那么這就有個(gè)問題了,既然在編譯的時(shí)候會(huì)在方法和類中擦除實(shí)際類型的信息,,那么在返回對(duì)象時(shí)又是如何知道其具體類型的呢,?如List<String>編譯后會(huì)擦除掉String信息,,那么在運(yùn)行時(shí)通過迭代器返回List中的對(duì)象時(shí),又是如何知道List中存儲(chǔ)的是String類型對(duì)象呢,?

       擦除在方法體中移除了類型信息,,所以在運(yùn)行時(shí)的問題就是邊界即對(duì)象進(jìn)入和離開方法的地點(diǎn),這正是編譯器在編譯期執(zhí)行類型檢查并插入轉(zhuǎn)型代碼的地點(diǎn),。泛型中的所有動(dòng)作都發(fā)生在邊界處:對(duì)傳遞進(jìn)來的值進(jìn)行額外的編譯期檢查,,并插入對(duì)傳遞出去的值的轉(zhuǎn)型。

 

3.泛型和子類型

為了徹底理解泛型,,這里看個(gè)例子:(Apple為Fruit的子類)

 

Java代碼  收藏代碼
  1. List<Apple> apples = new ArrayList<Apple>(); //1  
  2. List<Fruit> fruits = apples; //2  
 

第1行代碼顯然是對(duì)的,,但是第2行是否對(duì)呢?我們知道Fruit fruit = new Apple(),,這樣肯定是對(duì)的,,即蘋果肯定是水果,但是第2行在編譯的時(shí)候會(huì)出錯(cuò),。這會(huì)讓人比較納悶的是一個(gè)蘋果是水果,,為什么一箱蘋果就不是一箱水果了呢?可以這樣考慮,,我們假定第2行代碼沒有問題,,那么我們可以使用語句fruits.add(new Strawberry())(Strawberry為Fruit的子類)在fruits中加入草莓了,但是這樣的話,,一個(gè)List中裝入了各種不同類型的子類水果,,這顯然是不可以的,因?yàn)槲覀冊(cè)谌〕鯨ist中的水果對(duì)象時(shí),,就分不清楚到底該轉(zhuǎn)型為蘋果還是草莓了,。

通常來說,如果FooBar的子類型,,G是一種帶泛型的類型,,則G<Foo>不是G<Bar>的子類型,。這也許是泛型學(xué)習(xí)里面最讓人容易混淆的一點(diǎn),。

 

4.通配符

4.1通配符?

先看一個(gè)打印集合中所有元素的代碼,。

 

Java代碼  收藏代碼
  1. //不使用泛型  
  2. void printCollection(Collection c) {                  
  3. <span style="white-space: pre;">    </span>Iterator i=c.iterator();  
  4. <span style="white-space: pre;">    </span>for (k=0;k < c.size();k++) {  
  5. <span style="white-space: pre;">        </span>System.out.println(i.next());  
  6. <span style="white-space: pre;">    </span>}  
  7. }  
 

 

Java代碼  收藏代碼
  1. //使用泛型  
  2. void printCollection(Collection<Object> c) {  
  3. for (Object e:c) {  
  4. System.out.println(e);  
  5. }  
  6. }  
 

     很容易發(fā)現(xiàn),,使用泛型的版本只能接受元素類型為Object類型的集合如ArrayList<Object>();如果是ArrayList<String>,則會(huì)編譯時(shí)出錯(cuò),。因?yàn)槲覀兦懊嬲f過,,Collection<Object>并不是所有集合的超類。而老版本可以打印任何類型的集合,,那么如何改造新版本以便它能接受所有類型的集合呢,?這個(gè)問題可以通過使用通配符來解決,。修改后的代碼如下所示:

 

Java代碼  收藏代碼
  1. //使用通配符?,,表示可以接收任何元素類型的集合作為參數(shù)  
  2. void printCollection(Collection<?> c) {  
  3. <span style="white-space: pre;">    </span>for (Object e:c) {  
  4. <span style="white-space: pre;">        </span>System.out.println(e);  
  5. <span style="white-space: pre;">    </span>}  
  6. }  
 

這里使用了通配符,?指定可以使用任何類型的集合作為參數(shù)。讀取的元素使用了Object類型來表示,,這是安全的,,因?yàn)樗械念惗际荗bject的子類。這里就又出現(xiàn)了另外一個(gè)問題,,如下代碼所示,,如果試圖往使用通配符?的集合中加入對(duì)象,,就會(huì)在編譯時(shí)出現(xiàn)錯(cuò)誤,。需要注意的是,這里不管加入什么類型的對(duì)象都會(huì)出錯(cuò),。這是因?yàn)橥ㄅ浞??表示該集合存?chǔ)的元素類型未知,可以是任何類型,。往集合中加入元素需要是一個(gè)未知元素類型的子類型,,正因?yàn)樵摷洗鎯?chǔ)的元素類型未知,所以我們沒法向該集合中添加任何元素,。唯一的例外是null,,因?yàn)?span style="color: blue;">null是所有類型的子類型,所以盡管元素類型不知道,,但是null一定是它的子類型,。

 

Java代碼  收藏代碼
  1. Collection<?> c=new ArrayList<String>();  
  2. c.add(newObject()); //compile time error,不管加入什么對(duì)象都出錯(cuò),,除了null外,。  
  3. c.add(null); //OK  
 

另一方面,我們可以從List<?> lists中獲取對(duì)象,,雖然不知道List中存儲(chǔ)的是什么類型,,但是可以肯定的是存儲(chǔ)的類型一定是Object的子類型,所以可以用Object類型來獲取值,。如for(Object obj: lists),,這是合法的。

4.2邊界通配符

   1),?extends通配符

假定有一個(gè)畫圖的應(yīng)用,,可以畫各種形狀的圖形,如矩形和圓形等。為了在程序里面表示,,定義如下的類層次:

 

Java代碼  收藏代碼
  1. public abstract class Shape {  
  2. <span style="white-space: pre;">    </span>public abstract void draw(Canvas c);  
  3. }  
  4.   
  5. public class Circle extends Shape {  
  6. <span style="white-space: pre;">    </span>private int x,y,radius;  
  7. <span style="white-space: pre;">    </span>public void draw(Canvas c) { ... }  
  8. }  
  9.   
  10. public class Rectangle extends Shape  
  11. <span style="white-space: pre;">    </span>private int x,y,width,height;  
  12. <span style="white-space: pre;">    </span>public void draw(Canvasc) { ... }  
  13. }  
 

為了畫出集合中所有的形狀,,我們可以定義一個(gè)函數(shù),該函數(shù)接受帶有泛型的集合類對(duì)象作為參數(shù),。但是不幸的是,,我們只能接收元素類型為Shape的List對(duì)象,而不能接收類型為L(zhǎng)ist<Cycle>的對(duì)象,,這在前面已經(jīng)說過,。為了解決這個(gè)問題,所以有了邊界通配符的概念,。這里可以采用public void drawAll(List<? extends Shape>shapes)來滿足條件,,這樣就可以接收元素類型為Shape子類型的列表作為參數(shù)了。

 

Java代碼  收藏代碼
  1. //原始版本  
  2. public void drawAll(List<Shape> shapes) {  
  3. <span style="white-space: pre;">    </span>for (Shapes:shapes) {  
  4. <span style="white-space: pre;">        </span>s.draw(this);  
  5. <span style="white-space: pre;">    </span>}  
  6. }  
 

 

Java代碼  收藏代碼
  1. //使用邊界通配符的版本  
  2. public void drawAll(List<,?exends Shape> shapes) {  
  3. <span style="white-space: pre;">    </span>for (Shapes:shapes) {  
  4. <span style="white-space: pre;">        </span>s.draw(this);  
  5. <span style="white-space: pre;">    </span>}  
  6. }  
 

這里就又有個(gè)問題要注意了,,如果我們希望在List<?exends Shape> shapes中加入一個(gè)矩形對(duì)象,,如下所示:

shapes.add(0, new Rectangle()); //compile-time error

那么這時(shí)會(huì)出現(xiàn)一個(gè)編譯時(shí)錯(cuò)誤,,原因在于:我們只知道shapes中的元素時(shí)Shape類型的子類型,具體是什么子類型我們并不清楚,,所以我們不能往shapes中加入任何類型的對(duì)象,。不過我們?cè)谌〕銎渲袑?duì)象時(shí),可以使用Shape類型來取值,,因?yàn)殡m然我們不知道列表中的元素類型具體是什么類型,,但是我們肯定的是它一定是Shape類的子類型。

 

2),?super通配符

       這里還有一種邊界通配符為,?super。比如下面的代碼:

 

Java代碼  收藏代碼
  1. List<Shape> shapes = new ArrayList<Shape>();  
  2. List<? super Cicle> cicleSupers = shapes;  
  3. cicleSupers.add(new Cicle()); //OK, subclass of Cicle also OK  
  4. cicleSupers.add(new Shape()); //ERROR  
 

       這表示cicleSupers列表存儲(chǔ)的元素為Cicle的超類,,因此我們可以往其中加入Cicle對(duì)象或者Cicle的子類對(duì)象,,但是不能加入Shape對(duì)象。這里的原因在于列表cicleSupers存儲(chǔ)的元素類型為Cicle的超類,,但是具體是Cicle的什么超類并不清楚,。但是我們可以確定的是只要是Cicle或者Circle的子類,則一定是與該元素類別兼容,。

 

3)邊界通配符總結(jié)

<!--[if !supportLists]-->l         <!--[endif]-->如果你想從一個(gè)數(shù)據(jù)類型里獲取數(shù)據(jù),,使用 ? extends 通配符

<!--[if !supportLists]-->l         <!--[endif]-->如果你想把對(duì)象寫入一個(gè)數(shù)據(jù)結(jié)構(gòu)里,使用 ? super 通配符

<!--[if !supportLists]-->l         <!--[endif]-->如果你既想存,,又想取,那就別用通配符。

 

5.泛型方法

考慮實(shí)現(xiàn)一個(gè)方法,,該方法拷貝一個(gè)數(shù)組中的所有對(duì)象到集合中,。下面是初始的版本:

 

Java代碼  收藏代碼
  1. static void fromArrayToCollection(Object[]a, Collection<?> c) {  
  2. <span style="white-space: pre;">    </span>for (Object o:a) {  
  3. <span style="white-space: pre;">        </span>c.add(o); //compile time error  
  4. <span style="white-space: pre;">    </span>}  
  5. }  
 

可以看到顯然會(huì)出現(xiàn)編譯錯(cuò)誤,原因在之前有講過,,因?yàn)榧蟘中的類型未知,,所以不能往其中加入任何的對(duì)象(當(dāng)然,null除外),。解決該問題的一種比較好的辦法是使用泛型方法,,如下所示:

 

Java代碼  收藏代碼
  1. static <T> void fromArrayToCollection(T[] a, Collection<T>c){  
  2. <span style="white-space: pre;">    </span>for(T o : a) {  
  3. <span style="white-space: pre;">        </span>c.add(o);// correct  
  4. <span style="white-space: pre;">    </span>}  
  5. }  
 

注意泛型方法的格式,類型參數(shù)<T>需要放在函數(shù)返回值之前,。然后在參數(shù)和返回值中就可以使用泛型參數(shù)了,。具體一些調(diào)用方法的實(shí)例如下:

 

Java代碼  收藏代碼
  1. Object[] oa = new Object[100];  
  2. Collection<Object>co = new ArrayList<Object>();  
  3. fromArrayToCollection(oa, co);// T inferred to be Object  
  4. String[] sa = new String[100];  
  5. Collection<String>cs = new ArrayList<String>();  
  6. fromArrayToCollection(sa, cs);// T inferred to be String  
  7. fromArrayToCollection(sa, co);// T inferred to be Object  
  8. Integer[] ia = new Integer[100];  
  9. Float[] fa = new Float[100];  
  10. Number[] na = new Number[100];  
  11. Collection<Number>cn = new ArrayList<Number>();  
  12. fromArrayToCollection(ia, cn);// T inferred to be Number  
  13. fromArrayToCollection(fa, cn);// T inferred to be Number  
  14. fromArrayToCollection(na, cn);// T inferred to be Number  
  15. fromArrayToCollection(na, co);// T inferred to be Object  
  16. fromArrayToCollection(na, cs);// compile-time error  
 

注意到我們調(diào)用方法時(shí)并不需要傳遞類型參數(shù),系統(tǒng)會(huì)自動(dòng)判斷類型參數(shù)并調(diào)用合適的方法。當(dāng)然在某些情況下需要指定傳遞類型參數(shù),,比如當(dāng)存在與泛型方法相同的方法的時(shí)候(方法參數(shù)類型不一致),,如下面的一個(gè)例子:

 

Java代碼  收藏代碼
  1. public  <T> void go(T t) {  
  2.     System.out.println("generic function");  
  3. }  
  4. public void go(String str) {  
  5.     System.out.println("normal function");  
  6. }  
  7. public static void main(String[] args) {  
  8.         FuncGenric fg = new FuncGenric();  
  9.         fg.go("haha");//打印normal function  
  10.         fg.<String>go("haha");//打印generic function  
  11.         fg.go(new Object());//打印generic function  
  12.         fg.<Object>go(new Object());//打印generic function  
  13. }  
 

如例子中所示,當(dāng)不指定類型參數(shù)時(shí),,調(diào)用的是普通的方法,,如果指定了類型參數(shù),則調(diào)用泛型方法,??梢赃@樣理解,因?yàn)榉盒头椒ň幾g后類型擦除,,如果不指定類型參數(shù),,則泛型方法此時(shí)相當(dāng)于是public void go(Object t)。而普通的方法接收參數(shù)為String類型,,因此以String類型的實(shí)參調(diào)用函數(shù),,肯定會(huì)調(diào)用形參為String的普通方法了。如果是以O(shè)bject類型的實(shí)參調(diào)用函數(shù),,則會(huì)調(diào)用泛型方法,。

6.其他需要注意的小點(diǎn)

1)方法重載

在JAVA里面方法重載是不能通過返回值類型來區(qū)分的,比如代碼一中一個(gè)類中定義兩個(gè)如下的方法是不容許的,。但是當(dāng)參數(shù)為泛型類型時(shí),,卻是可以的。如下面代碼二中所示,,雖然形參經(jīng)過類型擦除后都為L(zhǎng)ist類型,,但是返回類型不同,這是可以的,。

 

Java代碼  收藏代碼
  1. /*代碼一:編譯時(shí)錯(cuò)誤*/   
  2. public class Erasure{  
  3.             public void test(int i){  
  4.                 System.out.println("Sting");  
  5.             }  
  6.             public int test(int i){  
  7.                 System.out.println("Integer");  
  8.             }  
  9.   }  
 

 

Java代碼  收藏代碼
  1. /*代碼二:正確 */  
  2.  public class Erasure{  
  3.             public void test(List<String> ls){  
  4.                 System.out.println("Sting");  
  5.             }  
  6.             public int test(List<Integer> li){  
  7.                 System.out.println("Integer");  
  8.             }  
  9.   }  
 

 

2)泛型類型是被所有調(diào)用共享的

       所有泛型類的實(shí)例都共享同一個(gè)運(yùn)行時(shí)類,,類型參數(shù)信息會(huì)在編譯時(shí)被擦除,。因此考慮如下代碼,雖然ArrayList<String>和ArrayList<Integer>類型參數(shù)不同,,但是他們都共享ArrayList類,,所以結(jié)果會(huì)是true。

      

 

Java代碼  收藏代碼
  1. List<String>l1 = new ArrayList<String>();  
  2. List<Integer>l2 = new ArrayList<Integer>();  
  3. System.out.println(l1.getClass() == l2.getClass()); //True  
 

 

3)instanceof

不能對(duì)確切的泛型類型使用instanceOf操作,。如下面的操作是非法的,,編譯時(shí)會(huì)出錯(cuò)。

 

Java代碼  收藏代碼
  1. Collection cs = new ArrayList<String>();  
  2. if (cs instanceof Collection<String>){…}// compile error.如果改成instanceof Collection<?>則不會(huì)出錯(cuò),。  
 

 

4)泛型數(shù)組問題

不能創(chuàng)建一個(gè)確切泛型類型的數(shù)組,。如下面代碼會(huì)出錯(cuò)。

List<String>[] lsa = new ArrayList<String>[10]; //compile error.

       因?yàn)槿绻梢赃@樣,,那么考慮如下代碼,,會(huì)導(dǎo)致運(yùn)行時(shí)錯(cuò)誤。

 

Java代碼  收藏代碼
  1. List<String>[] lsa = new ArrayList<String>[10]; // 實(shí)際上并不允許這樣創(chuàng)建數(shù)組  
  2. Object o = lsa;  
  3. Object[] oa = (Object[]) o;  
  4. List<Integer>li = new ArrayList<Integer>();  
  5. li.add(new Integer(3));  
  6. oa[1] = li;// unsound, but passes run time store check  
  7. String s = lsa[1].get(0); //run-time error - ClassCastException  
 

       因此只能創(chuàng)建帶通配符的泛型數(shù)組,,如下面例子所示,,這回可以通過編譯,但是在倒數(shù)第二行代碼中必須顯式的轉(zhuǎn)型才行,,即便如此,,最后還是會(huì)拋出類型轉(zhuǎn)換異常,因?yàn)榇鎯?chǔ)在lsa中的是List<Integer>類型的對(duì)象,,而不是List<String>類型,。最后一行代碼是正確的,類型匹配,,不會(huì)拋出異常,。

Java代碼  收藏代碼
  1. List<?>[] lsa = new List<?>[10]; // ok, array of unbounded wildcard type  
  2. Object o = lsa;  
  3. Object[] oa = (Object[]) o;  
  4. List<Integer>li = new ArrayList<Integer>();  
  5. li.add(new Integer(3));  
  6. oa[1] = li; //correct  
  7. String s = (String) lsa[1].get(0);// run time error, but cast is explicit  
  8. Integer it = (Integer)lsa[1].get(0); // OK   

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,,不代表本站觀點(diǎn),。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,,謹(jǐn)防詐騙,。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào),。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多