詳細(xì)全面解析Java泛型
1.概述
作為一個面向?qū)ο蟮木幊陶Z言,Java可以通過實(shí)現(xiàn)一些類,作為我們各種需求的一個模板,方便我們的使用。但有時候,這個類的范圍可能比我們想要的范圍要大,我們只想限定于滿足類的某些對象,那這樣的情況下,泛型的概念就被提出來了(非官方解釋,方便理解)。
舉個例子:比如我們我們生活中的車,它可以作為一個類,但是車其實(shí)又有很多種,包括貨車,轎車,大巴車等等,而其中的轎車外觀差不多,但是又屬于不同的品牌,這些品牌有很多不一樣的地方,這里我們可以把轎車的品牌看作是泛型(類似于標(biāo)簽)
通過上面的解釋,泛型的概念就比較清晰了,就是一種“類型參數(shù)”,所謂類型參數(shù)可以理解為將類型由原來的具體的類型進(jìn)行參數(shù)化,類似于方法中的變量參數(shù),此時類型也定義成參數(shù)形式(可以稱之為類型形參),然后在使用/調(diào)用時傳入具體的類型(類型實(shí)參)。
泛型的優(yōu)點(diǎn),不僅僅是上面提到的,其還有下面的優(yōu)點(diǎn)::
類型安全: 提高Java 程序的類型安全(泛型的主要目標(biāo))。
通過知道使用泛型定義的變量的類型限制,編譯器可以驗(yàn)證類型假設(shè)。
消除強(qiáng)制類型轉(zhuǎn)換:消除源代碼中的許多強(qiáng)制類型轉(zhuǎn)換。
這使得代碼的可讀性更高了,并且還減少了錯誤
上面說到了泛型在類中的使用,其實(shí)泛型的使用遠(yuǎn)不止于此,其還可以在在接口、方法中使用。下面就對這些分別進(jìn)行介紹
2.泛型類
所謂泛型類就是把當(dāng)我們在聲明類時,類中的有些成員的類型并不是確定,然后我們可以把泛型定義在類上,當(dāng)使用該類的時候,再把不確定成員的類型明確下來。
語法格式:
【修飾符】 class 類名<類型變量列表>{
//類體
}
注: <類型變量列表>:可以是一個或多個類型變量,一般都是使用單個的大寫字母表示。例如:、<K,V>等。
<類型變量列表>中的類型變量不能用于靜態(tài)成員上。
泛型類的使用:
使用這種類似于參數(shù)化類型的類時,在創(chuàng)建類的對象時候,我們需要注意:
- 指定類型變量對應(yīng)的實(shí)際類型參數(shù)
- 實(shí)際類型參數(shù)必須是引用數(shù)據(jù)類型,不能是基本數(shù)據(jù)類型
注:指定泛型實(shí)參時,必須左右兩邊一致,不存在多態(tài)現(xiàn)象(右邊的可以省略不寫)
代碼示例:
泛型類的聲明與使用:
public class Demo1 { ? ? public static void main(String[] args) { ? ? ? ? //泛型類的使用(<T>里面只能是引用類型) ? ? ? ? Student<Double> student1 = new Student<>("學(xué)生1",99.5); ? ? ? ? Student<String> student2 = new Student<>("學(xué)生2","優(yōu)秀"); ? ? ? ? Student<Character> student3 = new Student<>("學(xué)生3",'A'); ?? ??? ?//輸出結(jié)果 ?? ? ? ?System.out.println(student1); ? ? ? ? System.out.println(student2); ? ? ? ? System.out.println(student3); ? ? } } //泛型類的聲明 class Student<T> { //<T>這個就是泛型類的類型參數(shù) ? ? private String name; ? ? private T score; //使用泛型,定義分?jǐn)?shù)(分?jǐn)?shù)可能有double類型(99.5)、字符串類型(優(yōu)秀)、字符類型(‘A')等) ? ? //構(gòu)造方法 ? ? public Student() { ? ? } ? ? public Student(String name, T score) { ? ? ? ? this.name = name; ? ? ? ? this.score = score; ? ? } ? ? @Override ? ? public String toString() { ? ? ? ? return "Student{" + ? ? ? ? ? ? ? ? "name='" + name + '\'' + ? ? ? ? ? ? ? ? ", score=" + score + ? ? ? ? ? ? ? ? '}'; ? ? } }
2.1泛型接口
泛型接口和泛型類關(guān)系,就像接口和類的關(guān)系一樣。 這里不多說。
語法格式:
【修飾符】 interface 接口名<類型變量列表>{
}
注: <類型變量列表>:可以是一個或多個類型變量,一般都是使用單個的大寫字母表示。例如:、<K,V>等。
<類型變量列表>中的類型變量不能用于靜態(tài)成員上。
2.2泛型接口的使用
使用這種類似于參數(shù)化類型的接口時,我們需要注意:
指定類型變量對應(yīng)的實(shí)際類型參數(shù)
實(shí)際類型參數(shù)必須是引用數(shù)據(jù)類型,不能是基本數(shù)據(jù)類型
代碼示例
泛型接口的聲明與使用:
public class Demo1 { ? ? public static void main(String[] args) { ? ? ? ? //泛型類的使用(<T>里面只能是引用類型) ? ? ? ? Student<Double> student1 = new Student<>("學(xué)生1",99.5); ? ? ? ? //使用泛型接口 ? ? ? ? student1.print("學(xué)生1",99.5); ? ? } } //泛型類的聲明 class Student<T> implements Print<String,T>{ //<T>這個就是泛型類的,后面<String,T>是接口,多個類型變量 ? ? private String name; ? ? private T score; //使用泛型 ? ? //構(gòu)造方法 ? ? public Student() { ? ? } ? ? public Student(String name, T score) { ? ? ? ? this.name = name; ? ? ? ? this.score = score; ? ? } ? ? //重寫接口的方法 ? ? @Override ? ? public void print(String s, T t) { ? ? ? ? System.out.println("學(xué)生姓名:"+ this.name); ? ? ? ? System.out.println("學(xué)生成績:"+ this.score); ? ? } } //泛型接口的聲明 interface Print <T,V>{ ? ? //定義一個打印函數(shù),可以打印學(xué)生姓名和成績 ? ? public void print(T t, V v); }
3.類型變量的上限和下限
前面說到,我們可以使用泛型類型參數(shù),這樣等我們進(jìn)行實(shí)際使用的時候,我們可以任意使用類型,但如果想只使用某一系列的類型,泛型也是可以實(shí)現(xiàn)的。這就是我們說的類型變量的上限和類型變量的下限。下面進(jìn)行分別介紹。
3.1類型變量的上限
如果泛型類定義了類型變量的上限,那么該泛型類實(shí)際的類型只能是該上限類型或者其子類類型。
語法格式:
泛型類和泛型方法的用法是一樣的,后面都不再做區(qū)分。
<類型變量 extends 上限1 & 上限2> //上限可以有多個
注:如果多個上限中有類有接口,那么只能有一個類,而且必須寫在最左邊。接口的話,可以多個。
如果在聲明<類型變量>時沒有指定上限,默認(rèn)上限是java.lang.Object。
代碼示例:
類型變量的上限:
public class Demo2 { ? ? public static void main(String[] args) { ? ? ? ? Test<Double> test1 = new Test<>(77.5); //double類 // ? ? ? ?Test<String> test2 = new Test<String>(); 不是數(shù)字類的子類 ? ? ? ? Test<Integer> test3 = new Test<>(18); ? ? ? ? test1.print(77.5); ? ? ? ? test3.print(18); ? ? } } class Test<T extends Number >{ //數(shù)字類上限,只能使用數(shù)字類及其子類 ? ? private T num; ? ? public Test() { ? ? } ? ? public Test(T num) { ? ? ? ? this.num = num; ? ? } ? ? public void print(T num){ //測試方法 ? ? ? ? System.out.println(num); ? ? } }
3.2類型變量的下限
如果泛型類定義了類型變量的下限,那么該泛型類實(shí)際的類型只能是該下限類型或者其父類類型。
語法格式:
<? ?super E > // ? 代表接收E類型或者E的父類型的元素
? 是泛型類中的通配符(下面會講到,可以先看下面的再回來看這個)
代碼示例:
/* ? ? <? super 下限> ?*/ public class Demo5 { ? ? public static void main(String[] args){ ? ? ? ? C<String> c=new C<>(); ? ? ? ? c.setT("<? super 下限>"); ? ? ? ? fun1(c); ? ? } ??? ? ?//測試函數(shù),泛型類使用了下限 ? ? public static void fun1(C<? super String> c){ ?? ? //接受的數(shù)據(jù)類型只能為String、Object ? ? ? ? ? ? System.out.println(c.getT()); //輸入測試 ? ? } } class C<T>{ ? ? private T t; ? ? public T getT() { ? ? ? ? return t; ? ? } ? ? public void setT(T t) { ? ? ? ? this.t = t; ? ? } }
4.泛型方法
鑒于某個方法定義時,想要自己定義類型變量或者在某個靜態(tài)方法中定義類型變量的需求,JDK還提供了泛型方法的支持。即可以在某個方法定義時,自定以<類型變量>
注:前面說到類和接口上的類型形參是不能用于靜態(tài)方法
語法格式:
【修飾符】 <類型變量列表> 返回值類型 方法名(【形參列表】)【throws 異常列表】{
//方法體
}
注:- <類型變量列表>:可以是一個或多個類型變量,一般都是使用單個的大寫字母表示。例如: < T >、<K,V>等。
<類型變量>同樣也可以指定上限
代碼示例:
/* ? ? 泛型方法 ?*/ public class Demo3 { ? ? public static void main(String[] args) { ? ? ? ? Test1 test = new Test1(); //創(chuàng)建測試對象 ? ? ? ? test.print(12); //測試 ? ? ? ? test.print(12.5); //測試 ? ? } } class Test1{ ? ? public <T extends Number> void print(T t){ //泛型方法,可以設(shè)置上限 ? ? ? ? System.out.println("這是一個泛型方法,測試類型:" + t); ? ? } }
5.泛型擦除
泛型擦除只是在編譯階段才會有的,在實(shí)際運(yùn)行階段類型已經(jīng)確定了,這個時候就沒有泛型的概念了(JVM并不知道泛型的存在)。這個從有泛型信息到?jīng)]有泛型信息的過程稱之為“泛型擦除”。
其擦除規(guī)則如下:
- 若泛型類型沒有指定具體類型,用Object作為原始類型;
- 若有限定類型< T exnteds XClass >,使用XClass作為原始類型;
- 若有多個限定
< T exnteds XClass1 & XClass2 >
,使用第一個邊界類型XClass1作為原始類型;
6.類型通配符
通配符的意思是可以指代很多類型。這個主要使用在當(dāng)我們在聲明方法時,不確定該泛型實(shí)際類型的情況。
類型通配符有三種:
- <?> 任意類型
- <? extends 上限>
- <? super E>
下面對這三種通配符分別進(jìn)行介紹:
<?> 任意類型
當(dāng)泛型使用這種 類型通配符的時候,表示可以使用任意類型
代碼示例:
/* ? ? 類型通配符 ?*/ public class Demo4 { ? ? public static void main(String[] args) { ? ? ? ? // 語文老師使用時: ? ? ? ? StudentInfo<String> stu1 = new StudentInfo<String>("張三", "良好"); ? ? ? ? // 數(shù)學(xué)老師使用時: ? ? ? ? StudentInfo<Double> stu2 = new StudentInfo<Double>("張三", 90.5); ? ? ? ? // 英語老師使用時: ? ? ? ? StudentInfo<Character> stu3 = new StudentInfo<Character>("張三", 'C'); ? ? ? ? StudentInfo<?>[] arr = new StudentInfo[3]; //使用通配符 ? ? ? ? arr[0] = stu1; ? ? ? ? arr[1] = stu2; ? ? ? ? arr[2] = stu3; ? ? ? ? StudentInfoPrint.print(arr); //打印輸出結(jié)果 ? ? } } //學(xué)生類是一個參數(shù)化的泛型類 class StudentInfo<T>{ ? ? private String name; ? ? private T score; ? ? public StudentInfo() { ? ? ? ? super(); ? ? } ? ? public StudentInfo(String name, T score) { ? ? ? ? super(); ? ? ? ? this.name = name; ? ? ? ? this.score = score; ? ? } ? ? @Override ? ? public String toString() { ? ? ? ? return "姓名:" + name + ", 成績:" + score; ? ? } } //學(xué)生信息打印類 class StudentInfoPrint { ? ? //泛型方法,使用通配符 ? ? public static void print(StudentInfo<?>[] arr) { ? ? ? ? for (int i = 0; i < arr.length; i++) { ? ? ? ? ? ? System.out.println(arr[i]); ? ? ? ? } ? ? } }
<? extends 上限>
? 代表接收E類型或者E的子類型的元素
代碼示例
可參考上面的類型變量的上限代碼
<? super E>
? 代表接收E類型或者E的父類型的元素
代碼示例
可參考上面的類型變量的下限代碼
到此這篇關(guān)于詳細(xì)全面解析Java泛型的文章就介紹到這了,更多相關(guān)Java泛型內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Springboot實(shí)現(xiàn)郵件發(fā)送功能
這篇文章主要為大家詳細(xì)介紹了Springboot實(shí)現(xiàn)郵件發(fā)送功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下2020-02-02Spring Web MVC和Hibernate的集成配置詳解
這篇文章主要介紹了Spring Web MVC和Hibernate的集成配置詳解,具有一定借鑒價值,需要的朋友可以參考下2017-12-12Java實(shí)現(xiàn)藍(lán)橋杯數(shù)獨(dú)游戲的示例代碼
這篇文章主要介紹了Java實(shí)現(xiàn)藍(lán)橋杯數(shù)獨(dú)游戲的示例代碼,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-02-02Java的Hibernate框架中用于操作數(shù)據(jù)庫的HQL語句講解
這篇文章主要介紹了Java的Hibernate框架中用于操作數(shù)據(jù)庫的HQL語句講解,Hibernate是Java的SSH三大web開發(fā)框架之一,需要的朋友可以參考下2016-01-01Spring 定時任務(wù)@Scheduled 注解中的 Cron 表達(dá)式詳解
Cron 表達(dá)式是一種用于定義定時任務(wù)觸發(fā)時間的字符串表示形式,它由七個字段組成,分別表示秒、分鐘、小時、日期、月份、星期和年份,這篇文章主要介紹了Spring 定時任務(wù)@Scheduled 注解中的 Cron 表達(dá)式,需要的朋友可以參考下2023-07-07