詳談Java泛型中T和問號(通配符)的區(qū)別
類型本來有:簡單類型和復(fù)雜類型,引入泛型后把復(fù)雜類型分的更細(xì)了.
概述
泛型是Java SE 1.5的新特性,泛型的本質(zhì)是參數(shù)化類型,也就是說所操作的數(shù)據(jù)類型被指定為一個(gè)參數(shù)。這種參數(shù)類型可以用在類、接口和方法的創(chuàng)建中,分別稱為泛型類、泛型接口、泛型方法。 Java語言引入泛型的好處是安全簡單。
在Java SE 1.5之前,沒有泛型的情況的下,通過對類型Object的引用來實(shí)現(xiàn)參數(shù)的“任意化”,“任意化”帶來的缺點(diǎn)是要做顯式的強(qiáng)制類型轉(zhuǎn)換,而這種轉(zhuǎn)換是要求開發(fā)者對實(shí)際參數(shù)類型可以預(yù)知的情況下進(jìn)行的。對于強(qiáng)制類型轉(zhuǎn)換錯(cuò)誤的情況,編譯器可能不提示錯(cuò)誤,在運(yùn)行的時(shí)候才出現(xiàn)異常,這是一個(gè)安全隱患。
泛型的好處是在編譯的時(shí)候檢查類型安全,并且所有的強(qiáng)制轉(zhuǎn)換都是自動和隱式的,以提高代碼的重用率。
泛型的規(guī)則限制
泛型的類型參數(shù)只能是類類型(包括自定義類),不能是簡單類型。
同一種泛型可以對應(yīng)多個(gè)版本(因?yàn)閰?shù)類型是不確定的),不同版本的泛型類實(shí)例是不兼容的。
泛型的類型參數(shù)可以有多個(gè)。
泛型的參數(shù)類型可以使用extends語句,例如。習(xí)慣上稱為“有界類型”。
泛型的參數(shù)類型還可以是通配符類型。例如Class<?> classType = Class.forName(“java.lang.String”);
1、具體例子
下面給出兩個(gè)簡單的例子,實(shí)現(xiàn)同樣的功能,一個(gè)使用了泛型,一個(gè)沒有使用泛型。
例子一:使用了泛型
public class Gen<T> {
private T t;
public Gen(T t){
this.t = t;
}
public T getT() {
return t;
}
public void setT(T t) {
this.t = t;
}
public void showType(){
System.out.println("T的實(shí)際類型是:" + t.getClass().getName());
}
public static void main(String[] args) {
Gen<Integer> gen = new Gen<Integer>(1);
gen.showType();
int i = gen.getT();
System.out.println(" value = " + i);
System.out.println(" ====================== ");
//定義泛型類Gen的一個(gè)String的版本
Gen<String>strObj = new Gen<String>("Hello Gen!");
strObj.showType();
String s = strObj.getT();
System.out.println(" value = " + s);
}
}
例子二:沒有使用泛型
public class Gen2 {
// 定義一個(gè)通用類型成員
private Object obj;
public Gen2(Object obj) {
this.obj = obj;
}
public Object getObj() {
return obj;
}
public void setObj(Object obj) {
this.obj = obj;
}
public void showType() {
System.out.println("T的實(shí)際類型是: " + obj.getClass().getName());
}
public static void main(String[] args) {
// 定義類Gen2的一個(gè)Integer版本
Gen2 intObj = new Gen2(2);
intObj.showType();
int i = (Integer) intObj.getObj();
System.out.println(" value = " + i);
System.out.println(" ====================== ");
// 定義類Gen2的一個(gè)String版本
Gen2 strOb = new Gen2("Hello Gen!");
strOb.showType();
String s = (String) strOb.getObj();
System.out.println(" value= " + s);
}
}
2、深入泛型
在Java 5之前,為了讓類有通用性,往往將參數(shù)類型、返回類型設(shè)置為Object類型,當(dāng)獲取這些返回類型來使用時(shí)候,必須將其“強(qiáng)制”轉(zhuǎn)換為原有的類型或者接口,然后才可以調(diào)用對象上的方法。
泛型和使用“Object泛型”方式實(shí)現(xiàn)結(jié)果的完全一樣,但是簡單多了,因?yàn)椴恍枰獜?qiáng)制類型轉(zhuǎn)換。
泛型類語法:
使用來聲明一個(gè)類型持有者名稱,然后就可以把T當(dāng)作一個(gè)類型代表來聲明成員、參數(shù)和返回值類型。當(dāng)然T僅僅是個(gè)名字,這個(gè)名字可以自行定義。
class GenericsFoo 聲明了一個(gè)泛型類,這個(gè)T沒有任何限制,實(shí)際上相當(dāng)于Object類型,實(shí)際上相當(dāng)于 class GenericsFoo。
與Object泛型類相比,使用泛型所定義的類在聲明和構(gòu)造實(shí)例的時(shí)候,可以使用“<實(shí)際類型>”來一并指定泛型類型持有者的真實(shí)類型。例如:
GenericsFoo<Double> douFoo=new GenericsFoo<Double>(new Double("33"));
當(dāng)然,也可以在構(gòu)造對象的時(shí)候不使用尖括號指定泛型類型的真實(shí)類型,但是你在使用該對象的時(shí)候,就需要強(qiáng)制轉(zhuǎn)換了。比如:
GenericsFoo douFoo=new GenericsFoo(new Double("33"));
實(shí)際上,當(dāng)構(gòu)造對象時(shí)不指定類型信息的時(shí)候,默認(rèn)會使用Object類型,這也是要強(qiáng)制轉(zhuǎn)換的原因。
3、高級應(yīng)用
限制泛型
在上面的例子中,由于沒有限制class GenericsFoo類型持有者T的范圍,實(shí)際上這里的限定類型相當(dāng)于Object,這和“Object泛型”實(shí)質(zhì)是一樣的。限制比如我們要限制T為集合接口類型。只需要這么做:
class GenericsFoo,這樣類中的泛型T只能是Collection接口的實(shí)現(xiàn)類,傳入非Collection接口編譯會出錯(cuò)。
多接口限制
雖然Java泛型簡單的用 extends 統(tǒng)一的表示了原有的 extends 和 implements 的概念,但仍要遵循應(yīng)用的體系,Java 只能繼承一個(gè)類,但可以實(shí)現(xiàn)多個(gè)接口,所以你的某個(gè)類型需要用 extends 限定,且有多種類型的時(shí)候,只能存在一個(gè)是類,并且類寫在第一位,接口列在后面,也就是:
(泛型方法的類型限定)
<T extends SomeClass & interface1 & interface2 & interface3>
(泛型類中類型參數(shù)的限制)
public class Demo<T extends Comparable & Serializable> {
// T類型就可以用Comparable聲明的方法和Seriablizable所擁有的特性了
}
通配符泛型
為了解決類型被限制死了不能動態(tài)根據(jù)實(shí)例來確定的缺點(diǎn),引入了“通配符泛型”,針對上面的例子,使用通配泛型格式為<? extends Collection>,“?”代表未知類型,這個(gè)類型是實(shí)現(xiàn)Collection接口。
注意:
如果只指定了<?>,而沒有extends,則默認(rèn)是允許Object及其下的任何Java類了。也就是任意類。
通配符泛型不單可以向下限制,如<? extends Collection>,還可以向上限制,如<? super Double>,表示類型只能接受Double及其上層父類類型,如Number、Object類型的實(shí)例。
泛型類定義可以有多個(gè)泛型參數(shù),中間用逗號隔開,還可以定義泛型接口,泛型方法。這些都與泛型類中泛型的使用規(guī)則類似。
4、泛型方法
是否擁有泛型方法,與其所在的類是否泛型沒有關(guān)系。要定義泛型方法,只需將泛型參數(shù)列表置于返回值前。如:
public class GenericMethod {
public <T> void print(T x) {
System.out.println(x.getClass().getName());
}
public static void main(String[] args) {
GenericMethod method = new GenericMethod();
method.print(" ");
method.print(10);
method.print('a');
method.print(method);
}
}
需要注意的是,一個(gè)static方法,無法訪問泛型類的類型參數(shù),所以,若要static方法需要使用泛型能力,必須使其成為泛型方法。
泛型的好處如:
開始版本
public void write(Integer i, Integer[] ia); public void write(Double d, Double[] da);
泛型版本
public <T> void write(T t, T[] ta);
簡便了代碼
定義泛型
定義在類后面
緊跟類名后面
public class TestClassDefine<T, S extends T>{......}
定義泛型 T, S, 且S 繼承 T
定義在方法裝飾符后面
緊跟修飾符后面(public)
public <T, S extends T> T testGenericMethodDefine(T t, S s){......}
定義泛型 T, S, 且S 繼承 T
實(shí)例化泛型
實(shí)例化定義在類上的泛型
第一聲明類變量或者實(shí)例化時(shí)。例如
List<String> list; list = new ArrayList<String>;
第二繼承類或者實(shí)現(xiàn)接口時(shí)。例如
public class MyList<E> extends ArrayList<E> implements List<E> {...}
實(shí)例化定義方法上的泛型
當(dāng)調(diào)用范型方法時(shí),編譯器自動對類型參數(shù)(泛型)進(jìn)行賦值,當(dāng)不能成功賦值時(shí)報(bào)編譯錯(cuò)誤。
通配符(?)
上面有泛型的定義和賦值;當(dāng)在賦值的時(shí)候,上面一節(jié)說賦值的都是為具體類型,當(dāng)賦值的類型不確定的時(shí)候,我們用通配符(?)代替了:
List<?> unknownList; List<? extends Number> unknownNumberList; List<? super Integer> unknownBaseLineIntgerList;
以上這篇詳談Java泛型中T和問號(通配符)的區(qū)別就是小編分享給大家的全部內(nèi)容了,希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
java實(shí)現(xiàn)文件上傳的詳細(xì)步驟
文件上傳是用戶將本地文件通過Web頁面提交到服務(wù)器的過程,涉及客戶端、服務(wù)器端、上傳表單等組件,在SpringBoot中,通過MultipartFile接口處理上傳文件,并將其保存在服務(wù)器,文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下2024-10-10
背包問題-動態(tài)規(guī)劃java實(shí)現(xiàn)的分析與代碼
這篇文章主要給大家介紹了關(guān)于背包問題動態(tài)規(guī)劃java實(shí)現(xiàn)的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-12-12
Spring CGLlB動態(tài)代理實(shí)現(xiàn)過程解析
這篇文章主要介紹了Spring CGLlB動態(tài)代理實(shí)現(xiàn)過程解析,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-10-10
Java框架解說之BIO NIO AIO不同IO模型演進(jìn)之路
網(wǎng)上很多IO資料,對新手來說,越看越暈。根據(jù)自己的理解,總結(jié)對比了一下BIO、NIO、AIO,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-10-10
Java API如何實(shí)現(xiàn)向Hive批量導(dǎo)入數(shù)據(jù)
這篇文章主要介紹了Java API如何實(shí)現(xiàn)向Hive批量導(dǎo)入數(shù)據(jù)的操作,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-07-07

