java泛型通配符
【0】README
0.1)以下內容轉自: http://blog.csdn.net/baple/article/details/25056169
0.2) T 有類型 + ? 未知類型
一、通配符的上界(extends關鍵字)
- 既然知道List并不是List的子類型,那就需要去尋找替他解決的辦法, 是AnimalTrianer.act()方法變得更為通用(既可以接受List類型,也可以接受List等參數)。在java里解決辦法就是使用通配符“?”,具體到AnimalTrianer,就是將方法改為act(List<? extends Animal> list),當中“?”就是通配符,而“? extends Animal”則表示通配符“?”的上界為Animal,換句話說就是,“? extends Animal”可以代表Animal或其子類,可代表不了Animal的父類(如Object),因為通配符的上界是Animal。
- 如下,為改進之后的AnimalTrianer
- 再來測試一下,如下,發現Test 2 可以通過編譯了:
- 經過上述分析,可以知道List< Animal>和List< Cat>都是List< ? extends Animal>的子類型,類似有List< Bird>,List< Magpie>也是List< ? extends Animal>的子類型。
Conclusion)現總結如下,對于通配符的上界,有以下幾條基本規則:(假設給定的泛型類型為G,(如List中的List),兩個具體的泛型參數X、Y,當中Y是X的子類(如上的Animal和Cat))(干貨)
- C1) G< ? extends Y> 是 G< ? extends X>的子類型(如List< ? extends Cat> 或 List< Animal> 是 List< ? extends Animal>的子類型)。
- C2) G< X> 是 G< ? extends X>的子類型(如List< Animal> 是 List< ? extends Animal>的子類型)
C3) G< ?> 與 G< ? extends Object>等同,如List< ?> 與List< ? extends Object>等同。
學到這里,可能會遇到一些疑惑的地方,或者說事理解不透的地方,先觀察如下兩段代碼片段,判斷一下其是否可行??
對以上代碼的分析(Analysis): 因為“? extends Animal”可代表Animal或其子類(Bird,Cat),那上面的操作應該是可行的。事實上是”不行“,即無法通過編譯。為什么呢??
- A1)在解釋之前,再來重新強調一下已經知道的規則:在List< Animal> list里只能添加Animal類對象及其子類對象(如Cat和Bird對象),在List< Bird>里只能添加Bird類和其子類對象(如Magpie),可不能添加Animal對象(不是Bird的子類),類似的在List< Cat>和List< Magpie>里只能添加Cat和Bird對象(或其子類對象,不過這沒有列出)。
- A2)現在再回頭看一下testAdd()方法: 我們知道List< Animal>、List< Cat>等都是List<? extends Animal>的子類型。先假設傳入的參數為為List< Animal>,則第一段代碼的三個“add”操作都是可行的;可如果是List< Bird>呢??則只有第二個“add”可以執行;再假設傳入的是List< Tiger>(Tiger是想象出來的,可認為是Cat的子類),則三個“add”操作都不能執行。
- A3)現在反過來說:給testAdd傳入不同的參數,三個“add”操作都可能引發類型不兼容問題,而傳入的參數是未知的,所以java為了保護其類型一致,禁止向List< ? extends Animal>添加任意對象,不過卻可以添加null,即list.add(null)是可行的。
- A4)有了上面談到的基礎,再來理解第二段代碼就不難了: 因為List< ? extends Animal>的類型“? extends Animal”無法確定,可以是Animal,Bird或者Cat等,所以為了保護其類型的一致性,也是不能往list添加任意對象的,不過卻可以添加null。
先總結如下:不能往List< ? extends Animal> 添加任意對象,除了null。
- 另外提醒大家注意的一點是:在List< ? extends Animal> 可以是Animal類對象或Bird對象等(只是某一類對象),反過來說,在List< ? extends Animal> list里的都是Animal對象,即Bird也是Animal對象,Cat也是Animal對象(用java的語言來說就是子類可以指向父類,父類卻不能指向子類),那么在Animal里的所有方法都是可以調用的,如下:
for (Animal animal : list) { animal.eat(); }
二、通配符的下界(super關鍵字)
- 既然有了通配符的上界,自然有著通配符的下界。可以如此定義通配符的下界 List< ? super Bird>,其中”Bird“就是通配符的下界。
- Attention)注意:不能同時聲明泛型通配符申明上界和下界。
- 1)在談注意細節之前,我們先看一下通配符的使用規則——對于通配符的上界,有以下幾條基本規則:(假設給定的泛型類型為G,(如List中的List),兩個具體的泛型參數X、Y,當中Y是X的子類(如上的Animal和Cat))
G< ? super X> 是 G< ? super Y>的子類型(如List< ? super Animal> 是 List< ? super Bird>的子類型)。(干貨——List< ? super Animal> 是 List< ? super Bird>的子類型,其中Bird extends Animal)
G< X> 是 G< ? super X>的子類型(如List< Animal> 是 List< ? super Animal>的子類型)
(干貨——List< Animal> 是 List< ? super Animal>的子類型) - 2)現在再來看如下代碼,判斷其是否符合邏輯:
對以上代碼的分析(Analysis)
- A1)看第一段代碼,其分析如下:因為”? super Bird”代表了Bird或其父類,而Magpie是Bird的子類,所以上訴代碼不可通過編譯。而事實上是”行“,為什么呢?2?
- A2)在解疑之前,再來強調一個知識點,子類可以指向父類,即Bird也是Animal對象。現在考慮傳入到testAdd()的所有可能的參數,可以是List,List,或者List等等,發現這些參數的類型是Bird或其父類,那我們可以這樣看,把bird、magpie看成Bird對象,也可以將bird、magpie看成Animal對象,類似的可看成Object對象,最后發現這些添加到List< ? supe Bird> list里的對象都是同一類對象(如本文剛開篇提到的Test 1),因此testAdd方法是符合邏輯,可以通過編譯的。:
- A3)現在再來看一下第二段代碼: 對于,第二、三行代碼的解釋和上文一樣,至于最后一行“list.add(newAnimal(“animal”))”是無法通過編譯的,為什么的??為了保護類型的一致性,因為“? super Bird”可以是Animal,也可以是Object或其他Bird的父類,因無法確定其類型,也就不能往List< ? super Bird>添加Bird的任意父類對象。
- 既然無法確定其父類對象,那該如何遍歷List< ? super Bird> ? 因為Object是所有類的根類,所以可以用Object來遍歷。如下,不過貌似其意義不大。
for (Object object : list) {//…}
- 既然無法確定其父類對象,那該如何遍歷List< ? super Bird> ? 因為Object是所有類的根類,所以可以用Object來遍歷。如下,不過貌似其意義不大。
- A4)那“? super BoundingType”可以應用在什么地方呢??“? super BoundingType”應用相對廣泛,只不過是混合著用。下面舉個簡單的例子。先假設有以下兩個Student和CollegeStudent,當中CollegeStudent繼承Student,如下:
(干貨荔枝)
- 先需要根據他們的id對他們進行排序(注意此處是對數組對象進行排序),設計方法如下,(n指數組元素的個數):
- 先理解此方法含義,首先< T extends Comparable< T>>規定了數組中對象必須實現Comparable接口,Comparable< ? Super T>表示如果父類實現Comparable接口,其自身可不實現,如CollegeStudent。先假設有一個CollegeStudent的數組,如下:
- 執行方法 selectionSort(stu,4)是完全可以通過的。可如果定義的selectionSort方法如下:
- 則方法selectionSort(stu,4)不能執行,因為CollegeStudent沒有實現Comparable< CollegeStudent>接口。換句話就是“? super T”使selectionSort方法變得更為通用了。 (源代碼參見文末)
三、無界通配符
1)知道了通配符的上界和下界,其實也等同于知道了無界通配符,不加任何修飾即可,單獨一個“?”。如List< ?>,“?”可以代表任意類型,“任意”也就是未知類型。
2)無界通配符通常會用在下面兩種情況: (干貨——無界通配符通常會用在下面兩種情況)
- 2.1)當方法是使用原始的Object類型作為參數時,如下:
- 可以選擇改為如下實現:
- 這樣就可以兼容更多的輸出,而不單純是List,如下:
- 2.2)在定義的方法體的業務邏輯與泛型類型無關:如List.size,List.cleat。實際上,最常用的就是Class< ?>,因為Class< T>并沒有依賴于T。(干貨——這里又用到了java的反射機制)
- Attention)最后提醒一下的就是: List< Object>與List< ?>并不等同,List< Object>是List< ?>的子類。還有不能往List< ?> list里添加任意對象,除了null。
【4】 source code at a glance
(https://github.com/pacosonTang/core-java-volume/blob/master/chapter12/StudentTest.java)
package com.corejava.chapter12_4;import static java.lang.System.*; public class StudentTest {public static void main(String[] args){CollegeStudent[] stu = new CollegeStudent[]{new CollegeStudent(3),new CollegeStudent(2),new CollegeStudent(5),new CollegeStudent(4)};sortWithoutWildCard(stu);for(CollegeStudent cs : stu){out.println(cs.toString());}}// take insertion sort by id from small to larger order// T=CollegeStudent,然而CollegeStudent并沒有繼承Comparable<CollegeStudent>,// 僅僅是繼承了Comparable<Student>,且Comparable<Student> 與 Comparable<CollegeStudent> 并沒有任何關系// 即使 CollegeStudent extends Student// 但是這里并沒有報錯,不覺明里(理論上是說不通的,但實際上卻沒有報錯)public static <T extends Comparable<T>> void sortWithoutWildCard(T[] a){ // CompaT temp;int j;for (int i = 1; i < a.length; i++){temp = a[i];for (j = i; j > 0 && a[j-1].compareTo(temp) > 0; j--){a[j] = a[j-1];}a[j] = temp;}}// 這里T=CollegeStudent,理論上是說的通的(實際編譯也可以通過)// 因為這里的泛型方法的類型限定為 <T extends Comparable<? super T>>// 而 Comparable<Student> 是 Comparable<? super T> 的子類,且CollegeStudent extends Comparable<Student>public static <T extends Comparable<? super T>> void sortWithWildCard(T[] a){T temp;int j;for (int i = 1; i < a.length; i++){temp = a[i];for (j = i; j > 0 && a[j-1].compareTo(temp) > 0; j--){a[j] = a[j-1];}a[j] = temp;}} }class Student implements Comparable<Student> {private int id;public Student(int id){this.id = id;}@Overridepublic int compareTo(Student o){return this.id - o.id;}@Overridepublic String toString(){return id + " ";} }class CollegeStudent extends Student //implements Comparable<CollegeStudent> {public CollegeStudent(int id){super(id);} }總結
- 上一篇: java泛型——桥方法
- 下一篇: 《线性代数及其应用》