Java泛型定義與用法實(shí)例詳解
本文實(shí)例講述了Java泛型定義與用法。分享給大家供大家參考,具體如下:
1. 泛型的由來(lái)
先看如下代碼:
import java.util.List;
import java.util.ArrayList;
public class TestGeneric {
@SuppressWarnings({ "rawtypes", "unchecked" })
public static void main(String[] args) {
List list = new ArrayList();
list.add(1);
list.add("1");
list.add(new Object());
System.out.println(list);
// 取值
Integer var1 = (Integer) list.get(0);
String var2 = (String) list.get(1);
Object var3 = list.get(2);
System.out.println(var1 + " " + var2 + " " + var3);
}
}
運(yùn)行結(jié)果:
[1, 1, java.lang.Object@1db9742]
1 1 java.lang.Object@1db9742
這段代碼很簡(jiǎn)單,將整形、字符串、對(duì)象放進(jìn)list集合中,然后逐一取出??梢钥闯觯捎贚ist接口在定義時(shí)并不知道元素的類(lèi)型,因此默認(rèn)為Object,即任意類(lèi)型元素進(jìn)入list集合后都會(huì)自動(dòng)裝箱。而取值的過(guò)程更為復(fù)雜,所有取得的值都是裝箱后的Object對(duì)象,必須得知道每一個(gè)元素的初始類(lèi)型才能拆箱。一般使用集合的時(shí)候,集合的元素往往都是具有共同特征的,比如同屬于一類(lèi)的----那么,如果一開(kāi)始限定了list集合元素的類(lèi)型,那么就可避免上述不規(guī)范操作。代碼如下,
import java.util.List;
import java.util.ArrayList;
public class TestGeneric {
@SuppressWarnings("unused")
public static void main(String[] args) {
List<String> list = new ArrayList<String>();
// list.add(1);//報(bào)錯(cuò)
// list.add(new Object());//報(bào)錯(cuò)
list.add("1");
// 取值
String var1 = list.get(0);// 無(wú)需轉(zhuǎn)換
}
}
如此一來(lái),便有了泛型集合的說(shuō)法。實(shí)際上,查閱List接口的Api會(huì)發(fā)現(xiàn),List接口正是泛型接口,它可以接受一個(gè)類(lèi)型參數(shù)E,若不傳遞參數(shù),則默認(rèn)是Object類(lèi)型。
2. 泛型類(lèi)型的繼承關(guān)系
有如下功能的代碼,實(shí)現(xiàn)打印任意集合的元素:
import java.util.List;
import java.util.ArrayList;
import java.util.Collection;
public class TestGeneric{
//打印任意集合元素
public void print(Collection<Object> c){
System.out.println(c);
}
public static void main(String[] args){
List<String> list=new ArrayList<String>();
new TestGeneric().print(list);
}
}
輸出:
TestGeneric.java:11: 無(wú)法將 TestGeneric 中的 print(java.util.Collection<java.lang.Object>) 應(yīng)用于 (java.util.List<java.lang.String>)
new TestGeneric().print(list);
^
1 錯(cuò)誤
很明顯,意思就是傳遞的參數(shù)類(lèi)型不匹配。難道String不是繼承自O(shè)bject的嗎?沒(méi)錯(cuò),String是繼承自O(shè)bject的,但是List<String>與List<Object>是截然不同的兩個(gè)類(lèi)型,兩者之間沒(méi)有任何繼承關(guān)系。那如果真的要實(shí)現(xiàn)上面的功能,該如何呢?
2.1 類(lèi)型通配符
import java.util.List;
import java.util.ArrayList;
import java.util.Collection;
public class TestGeneric {
// 打印任意集合元素
public void print(Collection<?> c) {
System.out.println(c);
}
public static void main(String[] args) {
List<String> list = new ArrayList<String>();
new TestGeneric().print(list);
}
}
程序正常執(zhí)行,這里的?表示一個(gè)未知類(lèi)型,這個(gè)未知類(lèi)型與Object不同,List<?>代表了所有的List<類(lèi)型>的父類(lèi)。
2.2 泛型方法
不只有通配符可以解決泛型繼承的問(wèn)題,若將上面的方法定義為泛型方法也具有同樣的效果:
import java.util.List;
import java.util.ArrayList;
import java.util.Collection;
public class TestGeneric {
// 打印任意集合元素
public <T> void print(Collection<T> c) {
System.out.println(c);
}
public static void main(String[] args) {
List<String> list = new ArrayList<String>();
new TestGeneric().print(list);
}
}
泛型方法的定義形式如下,
修飾符 <T,E> 返回值 方法名(形參)
其中<T,E>在修飾符的后面做為類(lèi)型定義,為方法指明形參中需要用到的T,E類(lèi)型是來(lái)自哪里。既然泛型方法和類(lèi)型通配符都可以實(shí)現(xiàn)泛型中的繼承,那么有什么區(qū)別?
2.3 泛型方法和通配符的區(qū)別
看如下代碼:
import java.util.List;
import java.util.ArrayList;
import java.util.Collection;
public class TestGeneric {
// 打印任意集合元素
public <E, T extends E> void print(Collection<T> c1, Collection<E> c2) {
System.out.println(c1);
System.out.println(c2);
}
public static void main(String[] args) {
List<Father> list1 = new ArrayList<Father>();
List<Father> list2 = new ArrayList<Father>();
new TestGeneric().print(list1, list2);// 傳2個(gè)father類(lèi)型
List<Child> list3 = new ArrayList<Child>();
List<Father> list4 = new ArrayList<Father>();
new TestGeneric().print(list3, list4);// T為child,E為father
List<Father> list5 = new ArrayList<Father>();
List<Child> list6 = new ArrayList<Child>();
new TestGeneric().print(list5, list6);// T為father,E為child,報(bào)錯(cuò)
}
}
class Father {
}
class Child extends Father {
}
class Other {
}
上述泛型方法在定義T,E時(shí)已經(jīng)指明了關(guān)系:T是E的子類(lèi),所以在傳遞參數(shù)的時(shí)候,T要么是E的子類(lèi),要么就是E本身,所以在傳遞關(guān)系不小心變?yōu)镋 exends T時(shí),在第三次調(diào)用方法時(shí)報(bào)錯(cuò)了。而如果把上述代碼換成?通配符的話(huà),則不具有如此強(qiáng)的限定關(guān)系。
總之,泛型方法和?通配符都可以實(shí)現(xiàn)未知類(lèi)型的繼承,但是泛型方法主要強(qiáng)調(diào)多個(gè)未知類(lèi)型之間的依賴(lài)關(guān)系。如果只是單純用作成為一個(gè)通用類(lèi)型的父類(lèi)這一功能的話(huà),兩者都可以實(shí)現(xiàn),反而?通配符較為簡(jiǎn)潔明了。
2.4 泛型參數(shù)上、下限的注意
看如下代碼:
import java.util.List;
import java.util.ArrayList;
import java.util.Collection;
public class TestGeneric {
// 復(fù)制集合并返回原始集合的最后一個(gè)元素
public <T> T copy(Collection<T> des, Collection<? extends T> src) {
T lastElement = null;
for (T t : src) {
lastElement = t;
des.add(t);
}
return lastElement;
}
public static void main(String[] args) {
List<Number> des = new ArrayList<Number>();
List<Integer> src = new ArrayList<Integer>();
src.add(new Integer(1));
Integer lastElement = new TestGeneric().copy(des, src);//
System.out.println(lastElement.getClass());
}
}
輸出:
TestGeneric.java:18: 不兼容的類(lèi)型
找到: java.lang.Number
需要: java.lang.Integer
Integer lastElement= new TestGeneric().copy(des,src);//
^
1 錯(cuò)誤
當(dāng)調(diào)用完copy方法后,系統(tǒng)比對(duì)發(fā)現(xiàn)T類(lèi)型為Number,?類(lèi)型為Integer。所以函數(shù)返回的T類(lèi)型是Number了,所以根本不兼容Integer。要修改上面的代碼,有倆個(gè)辦法,
方法1:
改為
Number lastElement=new TestGeneric().copy(des,src);
分析代碼可以得出,?為T(mén)的子類(lèi),在方法中T=lastElement這句表現(xiàn)為多態(tài),雖然返回的是T類(lèi)型,但是多態(tài)的表現(xiàn)為?類(lèi)型,即Interger類(lèi)型,調(diào)用lastElement.getClass()也可發(fā)現(xiàn)返回的是java.lang.Integer類(lèi)型,說(shuō)明此處編譯類(lèi)型為T(mén)類(lèi)型,實(shí)際運(yùn)行類(lèi)型為?類(lèi)型。這就好比如下多態(tài)轉(zhuǎn)換,
Father f=new Child(); Child c=f;//此處一定報(bào)錯(cuò),類(lèi)型不兼容
雖然f的多態(tài)表現(xiàn)為子類(lèi)Child,但是上面一句連語(yǔ)法檢測(cè)都過(guò)不了。這也就是為什么上面Integer不能兼容Number的原因了。
方法2:
改為
public <T> T copy(Collection<? super T> des,Collection<T> src)
這樣一來(lái),?類(lèi)型變?yōu)榱烁割?lèi),T類(lèi)型變?yōu)榱俗宇?lèi),于是在方法中返回的T類(lèi)型對(duì)象,即lastElement就不具有多態(tài)性了。泛型中的上下限是很有學(xué)問(wèn)的,每次看源碼時(shí)都會(huì)琢磨很久,但還是會(huì)在浩瀚的接口+泛型的設(shè)計(jì)中昏迷,這種設(shè)計(jì)真的完全是為了突出面向?qū)ο蟮奶匦裕院舐聊グ伞?/p>
從這也再次可以看出?通配符在處理具有依賴(lài)關(guān)系的泛型方法中,顯得過(guò)于靈活而會(huì)導(dǎo)致一些潛在的隱患。
更多關(guān)于java算法相關(guān)內(nèi)容感興趣的讀者可查看本站專(zhuān)題:《Java數(shù)據(jù)結(jié)構(gòu)與算法教程》、《Java操作DOM節(jié)點(diǎn)技巧總結(jié)》、《Java文件與目錄操作技巧匯總》和《Java緩存操作技巧匯總》
希望本文所述對(duì)大家java程序設(shè)計(jì)有所幫助。
相關(guān)文章
Java查找不重復(fù)無(wú)序數(shù)組中是否存在兩個(gè)數(shù)字的和為某個(gè)值
今天小編就為大家分享一篇關(guān)于Java查找不重復(fù)無(wú)序數(shù)組中是否存在兩個(gè)數(shù)字的和為某個(gè)值,小編覺(jué)得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧2019-01-01
idea實(shí)現(xiàn)類(lèi)快捷生成接口方法示例
這篇文章主要介紹了idea實(shí)現(xiàn)類(lèi)快捷生成接口方法示例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-08-08
springboot如何靜態(tài)加載@configurationProperties
這篇文章主要介紹了springboot如何靜態(tài)加載@configurationProperties,本文一個(gè)錯(cuò)誤案例和成功案例結(jié)合實(shí)例代碼給大家介紹的非常詳細(xì),需要的朋友可以參考下2022-07-07
利用Java實(shí)現(xiàn)word導(dǎo)入導(dǎo)出富文本(含圖片)的詳細(xì)代碼
這篇文章主要為大家詳細(xì)介紹了利用Java實(shí)現(xiàn)word導(dǎo)入導(dǎo)出富文本(含圖片),文中的示例代碼講解詳細(xì),對(duì)大家的學(xué)習(xí)或工作有一定的幫助,感興趣的小伙伴可以學(xué)習(xí)一下2024-02-02
SWT(JFace) 文本編輯器 實(shí)現(xiàn)代碼
SWT(JFace) 文本編輯器 實(shí)現(xiàn)代碼2009-06-06
優(yōu)化spring?boot應(yīng)用后6s內(nèi)啟動(dòng)內(nèi)存減半
這篇文章主要為大家介紹了優(yōu)化spring?boot后應(yīng)用6s內(nèi)啟動(dòng)內(nèi)存減半的優(yōu)化示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步早日升職加薪2022-02-02
MyEclipse去除網(wǎng)上復(fù)制下來(lái)的代碼帶有的行號(hào)(正則去除行號(hào))
這篇文章主要介紹了MyEclipse去除網(wǎng)上復(fù)制下來(lái)的代碼帶有的行號(hào)(正則去除行號(hào))的相關(guān)資料,需要的朋友可以參考下2017-10-10

