Java 泛型詳解與范例
一、泛型的使用
前面我們學集合的時候,簡單的說過泛型的使用。如下:
ArrayList<Integer> list = new ArrayList<>(); Queue<Integer> queue = new LinkedList<>();
那么使用是這樣的簡單,該注意什么?
- 尖括號里的類型,只能寫引用類型
- 基礎(chǔ)數(shù)據(jù)類型的話,就需要寫相應的包裝類型
- 泛型只是編譯時期的一種機制,在運行時是沒有泛型的概念的。
二、泛型類的定義-類型邊界
泛型還有一個點就是:泛型的上界
。(類型形參 extends 類型邊界)有如下代碼:
public class Algorithm<T extends Comparable<T>> { public T findMax(T[] array) { } }
以上代碼中,方法的作用就是傳遞一個數(shù)組進去,要求返回這個數(shù)組中的最大值。
這個時候問題就來了,泛型是T類型,當調(diào)用這個方法的時候,傳遞過去的參數(shù)類型不一定就是簡單數(shù)據(jù)類型啊。那么這個時候該怎么進行判斷大小呢???
此時這樣的泛型寫法的作用就是:T extends Comparable, 就叫做泛型的上界
,當傳遞參數(shù)類型的時候,必須傳遞過去的參數(shù)類型必須是實現(xiàn)了Comparable接口的類型才可以。換句話說,傳遞過去的類型必須是可以進行比較的。
當我們自己定義的一個類Node,然后實現(xiàn)Comparable接口,就能調(diào)用如上的方法。
切記,這樣寫的泛型,傳遞過去的參數(shù)類型,必須是實現(xiàn)了Comparable接口的,當然也可以傳遞Comparable接口本身
。
三、類型擦除
類型擦除值得是:代碼編譯后,會將泛型T,全部擦除為Object類型。如下代碼:
ArrayList<Integer> list = new ArrayList<>();
上面這一行代碼,雖然此時寫的是Integer類型的,但是在編譯之后,JVM會自動地將Integer擦除為Object。即就是這樣子的:ArrayList。
那么可能就會有同學疑惑了,反正都要被擦除為Object類型,那干嘛還需要泛型這個東西???
那是因為有了泛型,可以讓代碼在編譯的時候,進行類型的檢查,就像上面的代碼,寫的是Integer類型的,那么傳遞過去的參數(shù)類型就必須是Integer類型才能通過編譯。做到了類型檢查,且在使用get方法,獲取某一個數(shù)值的時候,也會自動地將數(shù)據(jù)從Object轉(zhuǎn)換為Integer類型的。
以上的全部,只是解釋了只寫T類型的擦除機制。那么使用泛型的上界
的話,JVM就不會直接擦除為Object類型了,而是上界是什么類型,就擦除為什么類型即可。比如
public class Algorithm<T extends Comparable<T>> { }
如上的代碼,上界是Comparable接口,那么這個代碼形參部分,最多也只是這個接口了,也就是說,天花板就是這個接口。那么此時JVM在擦除的時候,直接擦除為Comparable接口類型即可。
總結(jié):類型擦除,主要還是要看類型的邊界。
四、泛型類的使用-通配符
在泛型中,?
稱為通配符。有如下代碼:
public class MyArrayList<E> { //自定義的一個類 } //main方法中的一個普通方法 public static void printAll(MyArrayList<?> list) { //打印傳遞過來的參數(shù)list }
以上代碼,可能大家看起來會怪怪的。簡單分析一下:
MyArrayList類型,是程序員自定義的一個類,采用了泛型。
而printAll方法,需要打印MyArrayList的數(shù)據(jù),但是MyArrayList是一個獨立的java文件,而printAll方法是在main方法中。二者并沒有任何聯(lián)系。那么此時在調(diào)用printAll時,形參部分就沒法進行定義了。
此時的話,就只能使用MyArrayList<?>類型作為形參部分,這樣定義的話,此時傳遞什么類型的MyArrayList,printAll方法都能夠進行接收。
有如下調(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類型 }
以上代碼,在運行的時候就不會報錯了。printAll方法,能夠接收所有類型的MyArrayList類的實例對象。
通配符的上界:<? extends 上界>
public static void printAll(MyArrayList<? extends Number> list) { }
如上代碼,是在原來代碼的基礎(chǔ)之上,添加了上界。原先的代碼是可以接收任何類型的MyArrayList實例對象。這里加了上界后,表示此時這個方法只能接收這個上界類型,以及上界的所有子類類型。
就拿上面這個代碼來說,上界是Number類型,那么此時這個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類型,此時String類型就會報錯,因為String不是Number的子類 printAll(list2); //Integer類型 printAll(list3); //Double類型 }
如上代碼,String類型的MyArrayList,調(diào)用printAll方法,就會報錯,因為String不是Number類的子類。
同理有了通配符的上界,那么也有通配符的下界。
通配符的下界:<? super 下界>
public static void printAll(MyArrayList<? super Integer> list) { }
同理,此時傳遞過去的MyArrayList實例對象的類型,只能是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); //此時String類型就會報錯,因為String的父類并不是Integer printAll(list3); //Double類型 //此時Double類型也會報錯,Double類型的父類并不是Integer printAll(list2); //Integer類型 printAll(list4); //Number類型 printAll(list5); //Object類型,是所有類的父類 }
以上代碼,就是通配符的下界。傳遞的參數(shù)類型,只能是該下界或者是下界的父類。
五、泛型方法
除了泛型類,還有一個泛型方法的概念。比較以下兩個代碼:
//泛型類的寫法 public class Algorithm<T extends Comparable<T>> { public T findMax(T[] array) { } }
此時我想把findMax方法,寫成靜態(tài)的。寫成靜態(tài)之后,這個方法就不依賴于對象了,只需要通過類名就能進行調(diào)用,可能你會覺得這還不簡單,你就寫下了以下代碼:
//猜想你會寫如下代碼--錯誤寫法 public class Algorithm<T extends Comparable<T>> { public static T findMax(T[] array) { } }
上面寫的代碼,肯定是不對的。正確的寫法如下:
//靜態(tài)方法的正確寫法 public class Algorithm { public static<T extends Comparable<T>> T findMax(T[] array) { } }
如上代碼,既然是寫泛型方法,我們只需將原先寫在類名后面的泛型,寫在static關(guān)鍵字后面即可。這樣寫就是一個泛型方法。
六、泛型的限制
- 泛型類型不支持基本數(shù)據(jù)類型,只能傳遞基本數(shù)據(jù)類型的包裝類
- 無法實例化泛型類型的對象。(比如new T)
- 無法使用泛型類型聲明靜態(tài)的屬性
- 無法使用instanceof判斷帶類型參數(shù)的泛型類型
- 無法創(chuàng)建泛型類的數(shù)組。(只能new Object[],然后強轉(zhuǎn))
- 無法create、catch、throw一個泛型類異常(異常不支持泛型)
- 泛型類型不是形參的一部分,無法重載
好啦,本期更新就到此結(jié)束啦,我們下期見吧?。?!
到此這篇關(guān)于Java 泛型詳解與范例的文章就介紹到這了,更多相關(guān)Java 泛型內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
mybatis框架order by作為參數(shù)傳入時失效的解決
這篇文章主要介紹了mybatis框架order by作為參數(shù)傳入時失效的解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-06-06Spring中@Import的各種用法以及ImportAware接口詳解
這篇文章主要介紹了Spring中@Import的各種用法以及ImportAware接口詳解,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2019-10-10