十年網(wǎng)站開發(fā)經(jīng)驗(yàn) + 多家企業(yè)客戶 + 靠譜的建站團(tuán)隊(duì)
量身定制 + 運(yùn)營(yíng)維護(hù)+專業(yè)推廣+無憂售后,網(wǎng)站問題一站解決
泛型的作用是指定集合里面所能存放的數(shù)據(jù)類型。
創(chuàng)新互聯(lián)專業(yè)為企業(yè)提供蒼南網(wǎng)站建設(shè)、蒼南做網(wǎng)站、蒼南網(wǎng)站設(shè)計(jì)、蒼南網(wǎng)站制作等企業(yè)網(wǎng)站建設(shè)、網(wǎng)頁(yè)設(shè)計(jì)與制作、蒼南企業(yè)網(wǎng)站模板建站服務(wù),十余年蒼南做網(wǎng)站經(jīng)驗(yàn),不只是建網(wǎng)站,更提供有價(jià)值的思路和整體網(wǎng)絡(luò)服務(wù)。
比如你定義一個(gè)用戶信息集合,里面用來存放用戶對(duì)象的。不允許存放其他類型的數(shù)據(jù),則可以定義一個(gè)list集合,泛型里面的類型為用戶對(duì)象User,這樣從里面取出來的對(duì)象就是一個(gè)User的對(duì)象了。
注解的使用的地方一般在類的頭部、方法的頭部、方法體里面使用,分別用來描述:
類的創(chuàng)建日期,作者,修改記錄,類的描述等信息;
方法的功能描述,參數(shù)描述等;
代碼塊的功能描述,變量的作用記錄等。
有泛型參數(shù),泛型方法,這篇文件寫的很好,你仔細(xì) 讀一下,可以多讀幾次,總會(huì)有收獲滴
java泛型
java泛型
什么是泛型?
泛型(Generic type 或者 generics)是對(duì) Java 語(yǔ)言的類型系統(tǒng)的一種擴(kuò)展,以支持創(chuàng)建可以按類型進(jìn)行參數(shù)化的類??梢园杨愋蛥?shù)看作是使用參數(shù)化類型時(shí)指定的類型的一個(gè)占位符,就像方法的形式參數(shù)是運(yùn)行時(shí)傳遞的值的占位符一樣。
可以在集合框架(Collection framework)中看到泛型的動(dòng)機(jī)。例如,Map 類允許您向一個(gè) Map 添加任意類的對(duì)象,即使最常見的情況是在給定映射(map)中保存某個(gè)特定類型(比如 String)的對(duì)象。
因?yàn)?Map.get() 被定義為返回 Object,所以一般必須將 Map.get() 的結(jié)果強(qiáng)制類型轉(zhuǎn)換為期望的類型,如下面的代碼所示:
Map m = new HashMap();
m.put("key", "blarg");
String s = (String) m.get("key");
要讓程序通過編譯,必須將 get() 的結(jié)果強(qiáng)制類型轉(zhuǎn)換為 String,并且希望結(jié)果真的是一個(gè) String。但是有可能某人已經(jīng)在該映射中保存了不是 String 的東西,這樣的話,上面的代碼將會(huì)拋出 ClassCastException。
理想情況下,您可能會(huì)得出這樣一個(gè)觀點(diǎn),即 m 是一個(gè) Map,它將 String 鍵映射到 String 值。這可以讓您消除代碼中的強(qiáng)制類型轉(zhuǎn)換,同時(shí)獲得一個(gè)附加的類型檢查層,該檢查層可以防止有人將錯(cuò)誤類型的鍵或值保存在集合中。這就是泛型所做的工作。
泛型的好處
Java 語(yǔ)言中引入泛型是一個(gè)較大的功能增強(qiáng)。不僅語(yǔ)言、類型系統(tǒng)和編譯器有了較大的變化,以支持泛型,而且類庫(kù)也進(jìn)行了大翻修,所以許多重要的類,比如集合框架,都已經(jīng)成為泛型化的了。這帶來了很多好處:
類型安全。 泛型的主要目標(biāo)是提高 Java 程序的類型安全。通過知道使用泛型定義的變量的類型限制,編譯器可以在一個(gè)高得多的程度上驗(yàn)證類型假設(shè)。沒有泛型,這些假設(shè)就只存在于程序員的頭腦中(或者如果幸運(yùn)的話,還存在于代碼注釋中)。
Java 程序中的一種流行技術(shù)是定義這樣的集合,即它的元素或鍵是公共類型的,比如“String 列表”或者“String 到 String 的映射”。通過在變量聲明中捕獲這一附加的類型信息,泛型允許編譯器實(shí)施這些附加的類型約束。類型錯(cuò)誤現(xiàn)在就可以在編譯時(shí)被捕獲了,而不是在運(yùn)行時(shí)當(dāng)作 ClassCastException 展示出來。將類型檢查從運(yùn)行時(shí)挪到編譯時(shí)有助于您更容易找到錯(cuò)誤,并可提高程序的可靠性。
消除強(qiáng)制類型轉(zhuǎn)換。 泛型的一個(gè)附帶好處是,消除源代碼中的許多強(qiáng)制類型轉(zhuǎn)換。這使得代碼更加可讀,并且減少了出錯(cuò)機(jī)會(huì)。
盡管減少?gòu)?qiáng)制類型轉(zhuǎn)換可以降低使用泛型類的代碼的羅嗦程度,但是聲明泛型變量會(huì)帶來相應(yīng)的羅嗦。比較下面兩個(gè)代碼例子。
該代碼不使用泛型:
List li = new ArrayList();
li.put(new Integer(3));
Integer i = (Integer) li.get(0);
該代碼使用泛型:
ListInteger li = new ArrayListInteger();
li.put(new Integer(3));
Integer i = li.get(0);
在簡(jiǎn)單的程序中使用一次泛型變量不會(huì)降低羅嗦程度。但是對(duì)于多次使用泛型變量的大型程序來說,則可以累積起來降低羅嗦程度。
潛在的性能收益。 泛型為較大的優(yōu)化帶來可能。在泛型的初始實(shí)現(xiàn)中,編譯器將強(qiáng)制類型轉(zhuǎn)換(沒有泛型的話,程序員會(huì)指定這些強(qiáng)制類型轉(zhuǎn)換)插入生成的字節(jié)碼中。但是更多類型信息可用于編譯器這一事實(shí),為未來版本的 JVM 的優(yōu)化帶來可能。
由于泛型的實(shí)現(xiàn)方式,支持泛型(幾乎)不需要 JVM 或類文件更改。所有工作都在編譯器中完成,編譯器生成類似于沒有泛型(和強(qiáng)制類型轉(zhuǎn)換)時(shí)所寫的代碼,只是更能確保類型安全而已。
泛型用法的例子
泛型的許多最佳例子都來自集合框架,因?yàn)榉盒妥屇诒4嬖诩现械脑厣现付愋图s束??紤]這個(gè)使用 Map 類的例子,其中涉及一定程度的優(yōu)化,即 Map.get() 返回的結(jié)果將確實(shí)是一個(gè) String:
Map m = new HashMap();
m.put("key", "blarg");
String s = (String) m.get("key");
如果有人已經(jīng)在映射中放置了不是 String 的其他東西,上面的代碼將會(huì)拋出 ClassCastException。泛型允許您表達(dá)這樣的類型約束,即 m 是一個(gè)將 String 鍵映射到 String 值的 Map。這可以消除代碼中的強(qiáng)制類型轉(zhuǎn)換,同時(shí)獲得一個(gè)附加的類型檢查層,這個(gè)檢查層可以防止有人將錯(cuò)誤類型的鍵或值保存在集合中。
下面的代碼示例展示了 JDK 5.0 中集合框架中的 Map 接口的定義的一部分:
public interface MapK, V {
public void put(K key, V value);
public V get(K key);
}
注意該接口的兩個(gè)附加物:
類型參數(shù) K 和 V 在類級(jí)別的規(guī)格說明,表示在聲明一個(gè) Map 類型的變量時(shí)指定的類型的占位符。
在 get()、put() 和其他方法的方法簽名中使用的 K 和 V。
為了贏得使用泛型的好處,必須在定義或?qū)嵗?Map 類型的變量時(shí)為 K 和 V 提供具體的值。以一種相對(duì)直觀的方式做這件事:
MapString, String m = new HashMapString, String();
m.put("key", "blarg");
String s = m.get("key");
當(dāng)使用 Map 的泛型化版本時(shí),您不再需要將 Map.get() 的結(jié)果強(qiáng)制類型轉(zhuǎn)換為 String,因?yàn)榫幾g器知道 get() 將返回一個(gè) String。
在使用泛型的版本中并沒有減少鍵盤錄入;實(shí)際上,比使用強(qiáng)制類型轉(zhuǎn)換的版本需要做更多鍵入。使用泛型只是帶來了附加的類型安全。因?yàn)榫幾g器知道關(guān)于您將放進(jìn) Map 中的鍵和值的類型的更多信息,所以類型檢查從執(zhí)行時(shí)挪到了編譯時(shí),這會(huì)提高可靠性并加快開發(fā)速度。
向后兼容
在 Java 語(yǔ)言中引入泛型的一個(gè)重要目標(biāo)就是維護(hù)向后兼容。盡管 JDK 5.0 的標(biāo)準(zhǔn)類庫(kù)中的許多類,比如集合框架,都已經(jīng)泛型化了,但是使用集合類(比如 HashMap 和 ArrayList)的現(xiàn)有代碼將繼續(xù)不加修改地在 JDK 5.0 中工作。當(dāng)然,沒有利用泛型的現(xiàn)有代碼將不會(huì)贏得泛型的類型安全好處。
二 泛型基礎(chǔ)
類型參數(shù)
在定義泛型類或聲明泛型類的變量時(shí),使用尖括號(hào)來指定形式類型參數(shù)。形式類型參數(shù)與實(shí)際類型參數(shù)之間的關(guān)系類似于形式方法參數(shù)與實(shí)際方法參數(shù)之間的關(guān)系,只是類型參數(shù)表示類型,而不是表示值。
泛型類中的類型參數(shù)幾乎可以用于任何可以使用類名的地方。例如,下面是 java.util.Map 接口的定義的摘錄:
public interface MapK, V {
public void put(K key, V value);
public V get(K key);
}
Map 接口是由兩個(gè)類型參數(shù)化的,這兩個(gè)類型是鍵類型 K 和值類型 V。(不使用泛型)將會(huì)接受或返回 Object 的方法現(xiàn)在在它們的方法簽名中使用 K 或 V,指示附加的類型約束位于 Map 的規(guī)格說明之下。
當(dāng)聲明或者實(shí)例化一個(gè)泛型的對(duì)象時(shí),必須指定類型參數(shù)的值:
MapString, String map = new HashMapString, String();
注意,在本例中,必須指定兩次類型參數(shù)。一次是在聲明變量 map 的類型時(shí),另一次是在選擇 HashMap 類的參數(shù)化以便可以實(shí)例化正確類型的一個(gè)實(shí)例時(shí)。
編譯器在遇到一個(gè) MapString, String 類型的變量時(shí),知道 K 和 V 現(xiàn)在被綁定為 String,因此它知道在這樣的變量上調(diào)用 Map.get() 將會(huì)得到 String 類型。
除了異常類型、枚舉或匿名內(nèi)部類以外,任何類都可以具有類型參數(shù)。
命名類型參數(shù)
推薦的命名約定是使用大寫的單個(gè)字母名稱作為類型參數(shù)。這與 C++ 約定有所不同(參閱 附錄 A:與 C++ 模板的比較),并反映了大多數(shù)泛型類將具有少量類型參數(shù)的假定。對(duì)于常見的泛型模式,推薦的名稱是:
K —— 鍵,比如映射的鍵。
V —— 值,比如 List 和 Set 的內(nèi)容,或者 Map 中的值。
E —— 異常類。
T —— 泛型。
泛型不是協(xié)變的
關(guān)于泛型的混淆,一個(gè)常見的來源就是假設(shè)它們像數(shù)組一樣是協(xié)變的。其實(shí)它們不是協(xié)變的。ListObject 不是 ListString 的父類型。
如果 A 擴(kuò)展 B,那么 A 的數(shù)組也是 B 的數(shù)組,并且完全可以在需要 B[] 的地方使用 A[]:
Integer[] intArray = new Integer[10];
Number[] numberArray = intArray;
上面的代碼是有效的,因?yàn)橐粋€(gè) Integer 是 一個(gè) Number,因而一個(gè) Integer 數(shù)組是 一個(gè) Number 數(shù)組。但是對(duì)于泛型來說則不然。下面的代碼是無效的:
ListInteger intList = new ArrayListInteger();
ListNumber numberList = intList; // invalid
最初,大多數(shù) Java 程序員覺得這缺少協(xié)變很煩人,或者甚至是“壞的(broken)”,但是之所以這樣有一個(gè)很好的原因。如果可以將 ListInteger 賦給 ListNumber,下面的代碼就會(huì)違背泛型應(yīng)該提供的類型安全:
ListInteger intList = new ArrayListInteger();
ListNumber numberList = intList; // invalid
numberList.add(new Float(3.1415));
因?yàn)?intList 和 numberList 都是有別名的,如果允許的話,上面的代碼就會(huì)讓您將不是 Integers 的東西放進(jìn) intList 中。但是,正如下一屏將會(huì)看到的,您有一個(gè)更加靈活的方式來定義泛型。
類型通配符
假設(shè)您具有該方法:
void printList(List l) {
for (Object o : l)
System.out.println(o);
}
上面的代碼在 JDK 5.0 上編譯通過,但是如果試圖用 ListInteger 調(diào)用它,則會(huì)得到警告。出現(xiàn)警告是因?yàn)椋鷮⒎盒停↙istInteger)傳遞給一個(gè)只承諾將它當(dāng)作 List(所謂的原始類型)的方法,這將破壞使用泛型的類型安全。
如果試圖編寫像下面這樣的方法,那么將會(huì)怎么樣?
void printList(ListObject l) {
for (Object o : l)
System.out.println(o);
}
它仍然不會(huì)通過編譯,因?yàn)橐粋€(gè) ListInteger 不是 一個(gè) ListObject(正如前一屏 泛型不是協(xié)變的 中所學(xué)的)。這才真正煩人 —— 現(xiàn)在您的泛型版本還沒有普通的非泛型版本有用!
解決方案是使用類型通配符:
void printList(List? l) {
for (Object o : l)
System.out.println(o);
}
上面代碼中的問號(hào)是一個(gè)類型通配符。它讀作“問號(hào)”。List? 是任何泛型 List 的父類型,所以您完全可以將 ListObject、ListInteger 或 ListListListFlutzpah 傳遞給 printList()。
類型通配符的作用
前一屏 類型通配符 中引入了類型通配符,這讓您可以聲明 List? 類型的變量。您可以對(duì)這樣的 List 做什么呢?非常方便,可以從中檢索元素,但是不能添加元素。原因不是編譯器知道哪些方法修改列表哪些方法不修改列表,而是(大多數(shù))變化的方法比不變化的方法需要更多的類型信息。下面的代碼則工作得很好:
ListInteger li = new ArrayListInteger();
li.add(new Integer(42));
List? lu = li;
System.out.println(lu.get(0));
為什么該代碼能工作呢?對(duì)于 lu,編譯器一點(diǎn)都不知道 List 的類型參數(shù)的值。但是編譯器比較聰明,它可以做一些類型推理。在本例中,它推斷未知的類型參數(shù)必須擴(kuò)展 Object。(這個(gè)特定的推理沒有太大的跳躍,但是編譯器可以作出一些非常令人佩服的類型推理,后面就會(huì)看到(在 底層細(xì)節(jié) 一節(jié)中)。所以它讓您調(diào)用 List.get() 并推斷返回類型為 Object。
另一方面,下面的代碼不能工作:
ListInteger li = new ArrayListInteger();
li.add(new Integer(42));
List? lu = li;
lu.add(new Integer(43)); // error
在本例中,對(duì)于 lu,編譯器不能對(duì) List 的類型參數(shù)作出足夠嚴(yán)密的推理,以確定將 Integer 傳遞給 List.add() 是類型安全的。所以編譯器將不允許您這么做。
以免您仍然認(rèn)為編譯器知道哪些方法更改列表的內(nèi)容哪些不更改列表內(nèi)容,請(qǐng)注意下面的代碼將能工作,因?yàn)樗灰蕾囉诰幾g器必須知道關(guān)于 lu 的類型參數(shù)的任何信息:
ListInteger li = new ArrayListInteger();
li.add(new Integer(42));
List? lu = li;
lu.clear();
泛型方法
(在 類型參數(shù) 一節(jié)中)您已經(jīng)看到,通過在類的定義中添加一個(gè)形式類型參數(shù)列表,可以將類泛型化。方法也可以被泛型化,不管它們定義在其中的類是不是泛型化的。
泛型類在多個(gè)方法簽名間實(shí)施類型約束。在 ListV 中,類型參數(shù) V 出現(xiàn)在 get()、add()、contains() 等方法的簽名中。當(dāng)創(chuàng)建一個(gè) MapK, V 類型的變量時(shí),您就在方法之間宣稱一個(gè)類型約束。您傳遞給 add() 的值將與 get() 返回的值的類型相同。
類似地,之所以聲明泛型方法,一般是因?yàn)槟胍谠摲椒ǖ亩鄠€(gè)參數(shù)之間宣稱一個(gè)類型約束。例如,下面代碼中的 ifThenElse() 方法,根據(jù)它的第一個(gè)參數(shù)的布爾值,它將返回第二個(gè)或第三個(gè)參數(shù):
public T T ifThenElse(boolean b, T first, T second) {
return b ? first : second;
}
注意,您可以調(diào)用 ifThenElse(),而不用顯式地告訴編譯器,您想要 T 的什么值。編譯器不必顯式地被告知 T 將具有什么值;它只知道這些值都必須相同。編譯器允許您調(diào)用下面的代碼,因?yàn)榫幾g器可以使用類型推理來推斷出,替代 T 的 String 滿足所有的類型約束:
String s = ifThenElse(b, "a", "b");
類似地,您可以調(diào)用:
Integer i = ifThenElse(b, new Integer(1), new Integer(2));
但是,編譯器不允許下面的代碼,因?yàn)闆]有類型會(huì)滿足所需的類型約束:
String s = ifThenElse(b, "pi", new Float(3.14));
為什么您選擇使用泛型方法,而不是將類型 T 添加到類定義呢?(至少)有兩種情況應(yīng)該這樣做:
當(dāng)泛型方法是靜態(tài)的時(shí),這種情況下不能使用類類型參數(shù)。
當(dāng) T 上的類型約束對(duì)于方法真正是局部的時(shí),這意味著沒有在相同類的另一個(gè) 方法簽名中使用相同 類型 T 的約束。通過使得泛型方法的類型參數(shù)對(duì)于方法是局部的,可以簡(jiǎn)化封閉類型的簽名。
有限制類型
在前一屏 泛型方法 的例子中,類型參數(shù) V 是無約束的或無限制的 類型。有時(shí)在還沒有完全指定類型參數(shù)時(shí),需要對(duì)類型參數(shù)指定附加的約束。
考慮例子 Matrix 類,它使用類型參數(shù) V,該參數(shù)由 Number 類來限制:
public class MatrixV extends Number { ... }
編譯器允許您創(chuàng)建 MatrixInteger 或 MatrixFloat 類型的變量,但是如果您試圖定義 MatrixString 類型的變量,則會(huì)出現(xiàn)錯(cuò)誤。類型參數(shù) V 被判斷為由 Number 限制 。在沒有類型限制時(shí),假設(shè)類型參數(shù)由 Object 限制。這就是為什么前一屏 泛型方法 中的例子,允許 List.get() 在 List? 上調(diào)用時(shí)返回 Object,即使編譯器不知道類型參數(shù) V 的類型。
三 一個(gè)簡(jiǎn)單的泛型類
編寫基本的容器類
此時(shí),您可以開始編寫簡(jiǎn)單的泛型類了。到目前為止,泛型類最常見的用例是容器類(比如集合框架)或者值持有者類(比如 WeakReference 或 ThreadLocal)。我們來編寫一個(gè)類,它類似于 List,充當(dāng)一個(gè)容器。其中,我們使用泛型來表示這樣一個(gè)約束,即 Lhist 的所有元素將具有相同類型。為了實(shí)現(xiàn)起來簡(jiǎn)單,Lhist 使用一個(gè)固定大小的數(shù)組來保存值,并且不接受 null 值。
Lhist 類將具有一個(gè)類型參數(shù) V(該參數(shù)是 Lhist 中的值的類型),并將具有以下方法:
public class LhistV {
public Lhist(int capacity) { ... }
public int size() { ... }
public void add(V value) { ... }
public void remove(V value) { ... }
public V get(int index) { ... }
}
要實(shí)例化 Lhist,只要在聲明時(shí)指定類型參數(shù)和想要的容量:
LhistString stringList = new LhistString(10);
實(shí)現(xiàn)構(gòu)造函數(shù)
在實(shí)現(xiàn) Lhist 類時(shí),您將會(huì)遇到的第一個(gè)攔路石是實(shí)現(xiàn)構(gòu)造函數(shù)。您可能會(huì)像下面這樣實(shí)現(xiàn)它:
public class LhistV {
private V[] array;
public Lhist(int capacity) {
array = new V[capacity]; // illegal
}
}
這似乎是分配后備數(shù)組最自然的一種方式,但是不幸的是,您不能這樣做。具體原因很復(fù)雜,當(dāng)學(xué)習(xí)到 底層細(xì)節(jié) 一節(jié)中的“擦除”主題時(shí),您就會(huì)明白。分配后備數(shù)組的實(shí)現(xiàn)方式很古怪且違反直覺。下面是構(gòu)造函數(shù)的一種可能的實(shí)現(xiàn)(該實(shí)現(xiàn)使用集合類所采用的方法):
public class LhistV {
private V[] array;
public Lhist(int capacity) {
array = (V[]) new Object[capacity];
}
}
另外,也可以使用反射來實(shí)例化數(shù)組。但是這樣做需要給構(gòu)造函數(shù)傳遞一個(gè)附加的參數(shù) —— 一個(gè)類常量,比如 Foo.class。后面在 ClassT 一節(jié)中將討論類常量。
實(shí)現(xiàn)方法
實(shí)現(xiàn) Lhist 的方法要容易得多。下面是 Lhist 類的完整實(shí)現(xiàn):
public class LhistV {
private V[] array;
private int size;
public Lhist(int capacity) {
array = (V[]) new Object[capacity];
}
public void add(V value) {
if (size == array.length)
throw new IndexOutOfBoundsException(Integer.toString(size));
else if (value == null)
throw new NullPointerException();
array[size++] = value;
}
public void remove(V value) {
int removalCount = 0;
for (int i=0; isize; i++) {
if (array[i].equals(value))
++removalCount;
else if (removalCount 0) {
array[i-removalCount] = array[i];
array[i] = null;
}
}
size -= removalCount;
}
public int size() { return size; }
public V get(int i) {
if (i = size)
throw new IndexOutOfBoundsException(Integer.toString(i));
return array[i];
}
}
注意,您在將會(huì)接受或返回 V 的方法中使用了形式類型參數(shù) V,但是您一點(diǎn)也不知道 V 具有什么樣的方法或域,因?yàn)檫@些對(duì)泛型代碼是不可知的。
使用 Lhist 類
使用 Lhist 類很容易。要定義一個(gè)整數(shù) Lhist,只需要在聲明和構(gòu)造函數(shù)中為類型參數(shù)提供一個(gè)實(shí)際值即可:
LhistInteger li = new LhistInteger(30);
編譯器知道,li.get() 返回的任何值都將是 Integer 類型,并且它還強(qiáng)制傳遞給 li.add() 或 li.remove() 的任何東西都是 Integer。除了實(shí)現(xiàn)構(gòu)造函數(shù)的方式很古怪之外,您不需要做任何十分特殊的事情以使 Lhist 是一個(gè)泛型類。
java 泛型是java SE 1.5的新特性,泛型的本質(zhì)是參數(shù)化類型,也就是說所操作的數(shù)據(jù)類型被指定為一個(gè)參數(shù)。這種參數(shù)類型可以用在類、接口和方法的創(chuàng)建中,分別稱為泛型類、泛型接口、泛型方法。
泛型(Generic type 或者 generics)是對(duì) Java 語(yǔ)言的類型系統(tǒng)的一種擴(kuò)展,以支持創(chuàng)建可以按類型進(jìn)行參數(shù)化的類??梢园杨愋蛥?shù)看作是使用參數(shù)化類型時(shí)指定的類型的一個(gè)占位符,就像方法的形式參數(shù)是運(yùn)行時(shí)傳遞的值的占位符一樣。
可以在集合框架(Collection framework)中看到泛型的動(dòng)機(jī)。例如,Map 類允許您向一個(gè) Map 添加任意類的對(duì)象,即使最常見的情況是在給定映射(map)中保存某個(gè)特定類型(比如 String)的對(duì)象。
因?yàn)?Map.get() 被定義為返回 Object,所以一般必須將 Map.get() 的結(jié)果強(qiáng)制類型轉(zhuǎn)換為期望的類型,如下面的代碼所示:
Map m = new HashMap();
m.put("key", "blarg");
String s = (String) m.get("key");
要讓程序通過編譯,必須將 get() 的結(jié)果強(qiáng)制類型轉(zhuǎn)換為 String,并且希望結(jié)果真的是一個(gè) String。但是有可能某人已經(jīng)在該映射中保存了不是 String 的東西,這樣的話,上面的代碼將會(huì)拋出 ClassCastException。
理想情況下,您可能會(huì)得出這樣一個(gè)觀點(diǎn),即 m 是一個(gè) Map,它將 String 鍵映射到 String 值。這可以讓您消除代碼中的強(qiáng)制類型轉(zhuǎn)換,同時(shí)獲得一個(gè)附加的類型檢查層,該檢查層可以防止有人將錯(cuò)誤類型的鍵或值保存在集合中。這就是泛型所做的工作。
泛型的好處
Java 語(yǔ)言中引入泛型是一個(gè)較大的功能增強(qiáng)。不僅語(yǔ)言、類型系統(tǒng)和編譯器有了較大的變化,以支持泛型,而且類庫(kù)也進(jìn)行了大翻修,所以許多重要的類,比如集合框架,都已經(jīng)成為泛型化的了。
這帶來了很多好處:
1,類型安全。 泛型的主要目標(biāo)是提高 Java 程序的類型安全。通過知道使用泛型定義的變量的類型限制,編譯器可以在一個(gè)高得多的程度上驗(yàn)證類型假設(shè)。沒有泛型,這些假設(shè)就只存在于程序員的頭腦中(或者如果幸運(yùn)的話,還存在于代碼注釋中)。
2,消除強(qiáng)制類型轉(zhuǎn)換。 泛型的一個(gè)附帶好處是,消除源代碼中的許多強(qiáng)制類型轉(zhuǎn)換。這使得代碼更加可讀,并且減少了出錯(cuò)機(jī)會(huì)。
3,潛在的性能收益。 泛型為較大的優(yōu)化帶來可能。在泛型的初始實(shí)現(xiàn)中,編譯器將強(qiáng)制類型轉(zhuǎn)換(沒有泛型的話,程序員會(huì)指定這些強(qiáng)制類型轉(zhuǎn)換)插入生成的字節(jié)碼中。但是更多類型信息可用于編譯器這一事實(shí),為未來版本的 JVM 的優(yōu)化帶來可能。由于泛型的實(shí)現(xiàn)方式,支持泛型(幾乎)不需要 JVM 或類文件更改。所有工作都在編譯器中完成,編譯器生成類似于沒有泛型(和強(qiáng)制類型轉(zhuǎn)換)時(shí)所寫的代碼,只是更能確保類型安全而已。
Java語(yǔ)言引入泛型的好處是安全簡(jiǎn)單。泛型的好處是在編譯的時(shí)候檢查類型安全,并且所有的強(qiáng)制轉(zhuǎn)換都是自動(dòng)和隱式的,提高代碼的重用率。
泛型在使用中還有一些規(guī)則和限制:
1、泛型的類型參數(shù)只能是類類型(包括自定義類),不能是簡(jiǎn)單類型。
2、同一種泛型可以對(duì)應(yīng)多個(gè)版本(因?yàn)閰?shù)類型是不確定的),不同版本的泛型類實(shí)例是不兼容的。
3、泛型的類型參數(shù)可以有多個(gè)。
4、泛型的參數(shù)類型可以使用extends語(yǔ)句,例如T extends superclass。習(xí)慣上成為“有界類型”。
5、泛型的參數(shù)類型還可以是通配符類型。例如Class? classType = Class.forName(Java.lang.String);
泛 型還有接口、方法等等,內(nèi)容很多,需要花費(fèi)一番功夫才能理解掌握并熟練應(yīng)用。在此給出我曾經(jīng)了解泛型時(shí)候?qū)懗龅膬蓚€(gè)例子(根據(jù)看的印象寫的),實(shí)現(xiàn)同樣的 功能,一個(gè)使用了泛型,一個(gè)沒有使用,通過對(duì)比,可以很快學(xué)會(huì)泛型的應(yīng)用,學(xué)會(huì)這個(gè)基本上學(xué)會(huì)了泛型70%的內(nèi)容。
泛型(Generictype或者generics)是對(duì)Java語(yǔ)言的類型系統(tǒng)的一種擴(kuò)展,以支持創(chuàng)建可以按類型進(jìn)行參數(shù)化的類??梢园杨愋蛥?shù)看作是使用參數(shù)化類型時(shí)指定的類型的一個(gè)占位符,就像方法的形式參數(shù)是運(yùn)行時(shí)傳遞的值的占位符一樣。\x0d\x0a可以在集合框架(Collectionframework)中看到泛型的動(dòng)機(jī)。例如,Map類允許您向一個(gè)Map添加任意類的對(duì)象,即使最常見的情況是在給定映射(map)中保存某個(gè)特定類型(比如String)的對(duì)象。\x0d\x0a因?yàn)镸ap.get()被定義為返回Object,所以一般必須將Map.get()的結(jié)果強(qiáng)制類型轉(zhuǎn)換為期望的類型,如下面的代碼所示:\x0d\x0a\x0d\x0aMapm=newHashMap();\x0d\x0a\x0d\x0am.put("key","blarg");\x0d\x0a\x0d\x0aStrings=(String)m.get("key");\x0d\x0a\x0d\x0a要讓程序通過編譯,必須將get()的結(jié)果強(qiáng)制類型轉(zhuǎn)換為String,并且希望結(jié)果真的是一個(gè)String。但是有可能某人已經(jīng)在該映射中保存了不是String的東西,這樣的話,上面的代碼將會(huì)拋出ClassCastException。\x0d\x0a理想情況下,您可能會(huì)得出這樣一個(gè)觀點(diǎn),即m是一個(gè)Map,它將String鍵映射到String值。這可以讓您消除代碼中的強(qiáng)制類型轉(zhuǎn)換,同時(shí)獲得一個(gè)附加的類型檢查層,該檢查層可以防止有人將錯(cuò)誤類型的鍵或值保存在集合中。這就是泛型所做的工作。\x0d\x0a泛型的好處\x0d\x0aJava語(yǔ)言中引入泛型是一個(gè)較大的功能增強(qiáng)。不僅語(yǔ)言、類型系統(tǒng)和編譯器有了較大的變化,以支持泛型,而且類庫(kù)也進(jìn)行了大翻修,所以許多重要的類,比如集合框架,都已經(jīng)成為泛型化的了。這帶來了很多好處:\x0d\x0a·類型安全。泛型的主要目標(biāo)是提高Java程序的類型安全。通過知道使用泛型定義的變量的類型限制,編譯器可以在一個(gè)高得多的程度上驗(yàn)證類型假設(shè)。沒有泛型,這些假設(shè)就只存在于程序員的頭腦中(或者如果幸運(yùn)的話,還存在于代碼注釋中)。\x0d\x0aJava程序中的一種流行技術(shù)是定義這樣的集合,即它的元素或鍵是公共類型的,比如“String列表”或者“String到String的映射”。通過在變量聲明中捕獲這一附加的類型信息,泛型允許編譯器實(shí)施這些附加的類型約束。類型錯(cuò)誤現(xiàn)在就可以在編譯時(shí)被捕獲了,而不是在運(yùn)行時(shí)當(dāng)作ClassCastException展示出來。將類型檢查從運(yùn)行時(shí)挪到編譯時(shí)有助于您更容易找到錯(cuò)誤,并可提高程序的可靠性。\x0d\x0a·消除強(qiáng)制類型轉(zhuǎn)換。泛型的一個(gè)附帶好處是,消除源代碼中的許多強(qiáng)制類型轉(zhuǎn)換。這使得代碼更加可讀,并且減少了出錯(cuò)機(jī)會(huì)。\x0d\x0a盡管減少?gòu)?qiáng)制類型轉(zhuǎn)換可以降低使用泛型類的代碼的羅嗦程度,但是聲明泛型變量會(huì)帶來相應(yīng)的羅嗦。比較下面兩個(gè)代碼例子。\x0d\x0a該代碼不使用泛型:\x0d\x0a\x0d\x0aListli=newArrayList();\x0d\x0a\x0d\x0ali.put(newInteger(3));\x0d\x0a\x0d\x0aIntegeri=(Integer)li.get(0);\x0d\x0a\x0d\x0a該代碼使用泛型:\x0d\x0a\x0d\x0aListli=newArrayList();\x0d\x0a\x0d\x0ali.put(newInteger(3));\x0d\x0a\x0d\x0aIntegeri=li.get(0);\x0d\x0a\x0d\x0a在簡(jiǎn)單的程序中使用一次泛型變量不會(huì)降低羅嗦程度。但是對(duì)于多次使用泛型變量的大型程序來說,則可以累積起來降低羅嗦程度。\x0d\x0a·潛在的性能收益。泛型為較大的優(yōu)化帶來可能。在泛型的初始實(shí)現(xiàn)中,編譯器將強(qiáng)制類型轉(zhuǎn)換(沒有泛型的話,程序員會(huì)指定這些強(qiáng)制類型轉(zhuǎn)換)插入生成的字節(jié)碼中。但是更多類型信息可用于編譯器這一事實(shí),為未來版本的JVM的優(yōu)化帶來可能。\x0d\x0a由于泛型的實(shí)現(xiàn)方式,支持泛型(幾乎)不需要JVM或類文件更改。所有工作都在編譯器中完成,編譯器生成類似于沒有泛型(和強(qiáng)制類型轉(zhuǎn)換)時(shí)所寫的代碼,只是更能確保類型安全而已。\x0d\x0a泛型用法的例子