hello !大家好!
今天的主題就是:泛型,在使用集合類時,大家就已經接觸到泛型了,那就是每個集合類后面的尖括號<>,這樣一對尖括號,在java中就稱為泛型,那么泛型這一個點,我們又該知道多少呢?我們往下看!
文章目錄
- 一、泛型的使用
- 二、泛型類的定義-型別邊界
- 三、型別擦除
- 四、泛型類的使用-通配符
- 五、泛型方法
- 六、泛型的限制
一、泛型的使用
前面我們學集合的時候,簡單的說過泛型的使用,如下:
ArrayList<Integer> list = new ArrayList<>();
Queue<Integer> queue = new LinkedList<>();
那么使用是這樣的簡單,該注意什么?
- 尖括號里的型別,只能寫參考型別
- 基礎資料型別的話,就需要寫相應的包裝型別
- 泛型只是編譯時期的一種機制,在運行時是沒有泛型的概念的,
二、泛型類的定義-型別邊界
泛型還有一個點就是:泛型的上界,(型別形參 extends 型別邊界)有如下代碼:
public class Algorithm<T extends Comparable<T>> {
public T findMax(T[] array) {
}
}
以上代碼中,方法的作用就是傳遞一個陣列進去,要求回傳這個陣列中的最大值,
這個時候問題就來了,泛型是T型別,當呼叫這個方法的時候,傳遞過去的引數型別不一定就是簡單資料型別啊,那么這個時候該怎么進行判斷大小呢???
此時這樣的泛型寫法的作用就是:T extends Comparable, 就叫做泛型的上界,當傳遞引數型別的時候,必須傳遞過去的引數型別必須是實作了Comparable介面的型別才可以,換句話說,傳遞過去的型別必須是可以進行比較的,
當我們自己定義的一個類Node,然后實作Comparable介面,就能呼叫如上的方法,
切記,這樣寫的泛型,傳遞過去的引數型別,必須是實作了Comparable介面的,當然也可以傳遞Comparable介面本身,
三、型別擦除
型別擦除值得是:代碼編譯后,會將泛型T,全部擦除為Object型別,如下代碼:
ArrayList<Integer> list = new ArrayList<>();
上面這一行代碼,雖然此時寫的是Integer型別的,但是在編譯之后,JVM會自動地將Integer擦除為Object,即就是這樣子的:ArrayList,
那么可能就會有同學疑惑了,反正都要被擦除為Object型別,那干嘛還需要泛型這個東西???
那是因為有了泛型,可以讓代碼在編譯的時候,進行型別的檢查,就像上面的代碼,寫的是Integer型別的,那么傳遞過去的引數型別就必須是Integer型別才能通過編譯,做到了型別檢查,且在使用get方法,獲取某一個數值的時候,也會自動地將資料從Object轉換為Integer型別的,
以上的全部,只是解釋了只寫T型別的擦除機制,那么使用泛型的上界的話,JVM就不會直接擦除為Object型別了,而是上界是什么型別,就擦除為什么型別即可,比如
public class Algorithm<T extends Comparable<T>> {
}
如上的代碼,上界是Comparable介面,那么這個代碼形參部分,最多也只是這個介面了,也就是說,天花板就是這個介面,那么此時JVM在擦除的時候,直接擦除為Comparable介面型別即可,
總結:型別擦除,主要還是要看型別的邊界,
四、泛型類的使用-通配符
在泛型中,?稱為通配符,有如下代碼:
public class MyArrayList<E> {
//自定義的一個類
}
//main方法中的一個普通方法
public static void printAll(MyArrayList<?> list) {
//列印傳遞過來的引數list
}
以上代碼,可能大家看起來會怪怪的,簡單分析一下:
MyArrayList型別,是程式員自定義的一個類,采用了泛型,
而printAll方法,需要列印MyArrayList的資料,但是MyArrayList是一個獨立的java檔案,而printAll方法是在main方法中,二者并沒有任何聯系,那么此時在呼叫printAll時,形參部分就沒法進行定義了,
此時的話,就只能使用MyArrayList<?>型別作為形參部分,這樣定義的話,此時傳遞什么型別的MyArrayList,printAll方法都能夠進行接收,
有如下呼叫代碼:
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) {
}
如上代碼,是在原來代碼的基礎之上,添加了上界,原先的代碼是可以接收任何型別的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,呼叫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型別,是所有類的父類
}
以上代碼,就是通配符的下界,傳遞的引數型別,只能是該下界或者是下界的父類,
五、泛型方法
除了泛型類,還有一個泛型方法的概念,比較以下兩個代碼:
//泛型類的寫法
public class Algorithm<T extends Comparable<T>> {
public T findMax(T[] array) {
}
}
此時我想把findMax方法,寫成靜態的,寫成靜態之后,這個方法就不依賴于物件了,只需要通過類名就能進行呼叫,可能你會覺得這還不簡單,你就寫下了以下代碼:
//猜想你會寫如下代碼--錯誤寫法
public class Algorithm<T extends Comparable<T>> {
public static T findMax(T[] array) {
}
}
上面寫的代碼,肯定是不對的,正確的寫法如下:
//靜態方法的正確寫法
public class Algorithm {
public static<T extends Comparable<T>> T findMax(T[] array) {
}
}
如上代碼,既然是寫泛型方法,我們只需將原先寫在類名后面的泛型,寫在static關鍵字后面即可,這樣寫就是一個泛型方法,
六、泛型的限制
- 泛型型別不支持基本資料型別,只能傳遞基本資料型別的包裝類
- 無法實體化泛型型別的物件,(比如new T)
- 無法使用泛型型別宣告靜態的屬性
- 無法使用instanceof判斷帶型別引數的泛型型別
- 無法創建泛型類的陣列,(只能new Object[],然后強轉)
- 無法create、catch、throw一個泛型類例外(例外不支持泛型)
- 泛型型別不是形參的一部分,無法多載
好啦,本期更新就到此結束啦,我們下期見吧!!!
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/352205.html
標籤:java
