Java 泛型詳解與范例
一、泛型的使用
前面我們學(xué)集合的時(shí)候,簡(jiǎn)單的說(shuō)過(guò)泛型的使用。如下:
ArrayList<Integer> list = new ArrayList<>(); Queue<Integer> queue = new LinkedList<>();
那么使用是這樣的簡(jiǎn)單,該注意什么?
- 尖括號(hào)里的類型,只能寫引用類型
- 基礎(chǔ)數(shù)據(jù)類型的話,就需要寫相應(yīng)的包裝類型
- 泛型只是編譯時(shí)期的一種機(jī)制,在運(yùn)行時(shí)是沒(méi)有泛型的概念的。
二、泛型類的定義-類型邊界
泛型還有一個(gè)點(diǎn)就是:泛型的上界。(類型形參 extends 類型邊界)有如下代碼:
public class Algorithm<T extends Comparable<T>> {
public T findMax(T[] array) {
}
}
以上代碼中,方法的作用就是傳遞一個(gè)數(shù)組進(jìn)去,要求返回這個(gè)數(shù)組中的最大值。
這個(gè)時(shí)候問(wèn)題就來(lái)了,泛型是T類型,當(dāng)調(diào)用這個(gè)方法的時(shí)候,傳遞過(guò)去的參數(shù)類型不一定就是簡(jiǎn)單數(shù)據(jù)類型啊。那么這個(gè)時(shí)候該怎么進(jìn)行判斷大小呢???
此時(shí)這樣的泛型寫法的作用就是:T extends Comparable, 就叫做泛型的上界,當(dāng)傳遞參數(shù)類型的時(shí)候,必須傳遞過(guò)去的參數(shù)類型必須是實(shí)現(xiàn)了Comparable接口的類型才可以。換句話說(shuō),傳遞過(guò)去的類型必須是可以進(jìn)行比較的。
當(dāng)我們自己定義的一個(gè)類Node,然后實(shí)現(xiàn)Comparable接口,就能調(diào)用如上的方法。
切記,這樣寫的泛型,傳遞過(guò)去的參數(shù)類型,必須是實(shí)現(xiàn)了Comparable接口的,當(dāng)然也可以傳遞Comparable接口本身。
三、類型擦除
類型擦除值得是:代碼編譯后,會(huì)將泛型T,全部擦除為Object類型。如下代碼:
ArrayList<Integer> list = new ArrayList<>();
上面這一行代碼,雖然此時(shí)寫的是Integer類型的,但是在編譯之后,JVM會(huì)自動(dòng)地將Integer擦除為Object。即就是這樣子的:ArrayList。
那么可能就會(huì)有同學(xué)疑惑了,反正都要被擦除為Object類型,那干嘛還需要泛型這個(gè)東西???
那是因?yàn)橛辛朔盒停梢宰尨a在編譯的時(shí)候,進(jìn)行類型的檢查,就像上面的代碼,寫的是Integer類型的,那么傳遞過(guò)去的參數(shù)類型就必須是Integer類型才能通過(guò)編譯。做到了類型檢查,且在使用get方法,獲取某一個(gè)數(shù)值的時(shí)候,也會(huì)自動(dòng)地將數(shù)據(jù)從Object轉(zhuǎn)換為Integer類型的。
以上的全部,只是解釋了只寫T類型的擦除機(jī)制。那么使用泛型的上界的話,JVM就不會(huì)直接擦除為Object類型了,而是上界是什么類型,就擦除為什么類型即可。比如
public class Algorithm<T extends Comparable<T>> {
}
如上的代碼,上界是Comparable接口,那么這個(gè)代碼形參部分,最多也只是這個(gè)接口了,也就是說(shuō),天花板就是這個(gè)接口。那么此時(shí)JVM在擦除的時(shí)候,直接擦除為Comparable接口類型即可。
總結(jié):類型擦除,主要還是要看類型的邊界。
四、泛型類的使用-通配符
在泛型中,?稱為通配符。有如下代碼:
public class MyArrayList<E> {
//自定義的一個(gè)類
}
//main方法中的一個(gè)普通方法
public static void printAll(MyArrayList<?> list) {
//打印傳遞過(guò)來(lái)的參數(shù)list
}
以上代碼,可能大家看起來(lái)會(huì)怪怪的。簡(jiǎn)單分析一下:
MyArrayList類型,是程序員自定義的一個(gè)類,采用了泛型。
而printAll方法,需要打印MyArrayList的數(shù)據(jù),但是MyArrayList是一個(gè)獨(dú)立的java文件,而printAll方法是在main方法中。二者并沒(méi)有任何聯(lián)系。那么此時(shí)在調(diào)用printAll時(shí),形參部分就沒(méi)法進(jìn)行定義了。
此時(shí)的話,就只能使用MyArrayList<?>類型作為形參部分,這樣定義的話,此時(shí)傳遞什么類型的MyArrayList,printAll方法都能夠進(jìn)行接收。
有如下調(diào)用代碼:
public static void main(String[] args) {
MyArrayList<String> list1 = new MyArrayList<>();
MyArrayList<Integer> list2 = new MyArrayList<>();
MyArrayList<Double> list3 = new MyArrayList<>();
printAll(list1); //String類型
printAll(list2); //Integer類型
printAll(list3); //Double類型
}
以上代碼,在運(yùn)行的時(shí)候就不會(huì)報(bào)錯(cuò)了。printAll方法,能夠接收所有類型的MyArrayList類的實(shí)例對(duì)象。
通配符的上界:<? extends 上界>
public static void printAll(MyArrayList<? extends Number> list) {
}
如上代碼,是在原來(lái)代碼的基礎(chǔ)之上,添加了上界。原先的代碼是可以接收任何類型的MyArrayList實(shí)例對(duì)象。這里加了上界后,表示此時(shí)這個(gè)方法只能接收這個(gè)上界類型,以及上界的所有子類類型。
就拿上面這個(gè)代碼來(lái)說(shuō),上界是Number類型,那么此時(shí)這個(gè)printAll方法能夠接收的形參類型就只能是Number或者Number的子類類型。
public static void main(String[] args) {
MyArrayList<String> list1 = new MyArrayList<>();
MyArrayList<Integer> list2 = new MyArrayList<>();
MyArrayList<Double> list3 = new MyArrayList<>();
//printAll(list1);
//String類型,此時(shí)String類型就會(huì)報(bào)錯(cuò),因?yàn)镾tring不是Number的子類
printAll(list2); //Integer類型
printAll(list3); //Double類型
}
如上代碼,String類型的MyArrayList,調(diào)用printAll方法,就會(huì)報(bào)錯(cuò),因?yàn)镾tring不是Number類的子類。
同理有了通配符的上界,那么也有通配符的下界。
通配符的下界:<? super 下界>
public static void printAll(MyArrayList<? super Integer> list) {
}
同理,此時(shí)傳遞過(guò)去的MyArrayList實(shí)例對(duì)象的類型,只能是Integer類型,或者是Integer類型的父類。
public static void main(String[] args) {
MyArrayList<String> list1 = new MyArrayList<>();
MyArrayList<Integer> list2 = new MyArrayList<>();
MyArrayList<Double> list3 = new MyArrayList<>();
MyArrayList<Number> list4 = new MyArrayList<>();
MyArrayList<Object> list5 = new MyArrayList<>();
//printAll(list1);
//此時(shí)String類型就會(huì)報(bào)錯(cuò),因?yàn)镾tring的父類并不是Integer
printAll(list3); //Double類型
//此時(shí)Double類型也會(huì)報(bào)錯(cuò),Double類型的父類并不是Integer
printAll(list2); //Integer類型
printAll(list4); //Number類型
printAll(list5); //Object類型,是所有類的父類
}
以上代碼,就是通配符的下界。傳遞的參數(shù)類型,只能是該下界或者是下界的父類。
五、泛型方法
除了泛型類,還有一個(gè)泛型方法的概念。比較以下兩個(gè)代碼:
//泛型類的寫法
public class Algorithm<T extends Comparable<T>> {
public T findMax(T[] array) {
}
}
此時(shí)我想把findMax方法,寫成靜態(tài)的。寫成靜態(tài)之后,這個(gè)方法就不依賴于對(duì)象了,只需要通過(guò)類名就能進(jìn)行調(diào)用,可能你會(huì)覺(jué)得這還不簡(jiǎn)單,你就寫下了以下代碼:
//猜想你會(huì)寫如下代碼--錯(cuò)誤寫法
public class Algorithm<T extends Comparable<T>> {
public static T findMax(T[] array) {
}
}
上面寫的代碼,肯定是不對(duì)的。正確的寫法如下:
//靜態(tài)方法的正確寫法
public class Algorithm {
public static<T extends Comparable<T>> T findMax(T[] array) {
}
}
如上代碼,既然是寫泛型方法,我們只需將原先寫在類名后面的泛型,寫在static關(guān)鍵字后面即可。這樣寫就是一個(gè)泛型方法。
六、泛型的限制
- 泛型類型不支持基本數(shù)據(jù)類型,只能傳遞基本數(shù)據(jù)類型的包裝類
- 無(wú)法實(shí)例化泛型類型的對(duì)象。(比如new T)
- 無(wú)法使用泛型類型聲明靜態(tài)的屬性
- 無(wú)法使用instanceof判斷帶類型參數(shù)的泛型類型
- 無(wú)法創(chuàng)建泛型類的數(shù)組。(只能new Object[],然后強(qiáng)轉(zhuǎn))
- 無(wú)法create、catch、throw一個(gè)泛型類異常(異常不支持泛型)
- 泛型類型不是形參的一部分,無(wú)法重載
好啦,本期更新就到此結(jié)束啦,我們下期見(jiàn)吧!??!
到此這篇關(guān)于Java 泛型詳解與范例的文章就介紹到這了,更多相關(guān)Java 泛型內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Spring Cloud Stream 高級(jí)特性使用詳解
這篇文章主要為大家介紹了Spring Cloud Stream 高級(jí)特性使用詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-09-09
Java實(shí)現(xiàn)視頻初步壓縮和解壓的代碼示例
從攝像頭讀取每一幀的圖片,用一些簡(jiǎn)單的方法將多張圖片信息壓縮到一份文件中(自定義的視頻文件),自定義解碼器讀取視頻文件,并將每幀圖片展示成視頻,本文主要介紹了Java實(shí)現(xiàn)視頻初步壓縮和解壓,需要的朋友可以參考下2023-10-10
IDEA中SpringBoot項(xiàng)目數(shù)據(jù)庫(kù)連接加密方法
這篇文章主要介紹了IDEA中SpringBoot項(xiàng)目數(shù)據(jù)庫(kù)連接加密方法,文章通過(guò)圖文結(jié)合的方式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作有一定的幫助,需要的朋友可以參考下2024-06-06

