轉自:http://www.linuxidc.com/Linux/2013-10/90928.htmjava
? 未知類型linux
既然知道List<Cat>並非List<Anilmal>的子類型,那就須要去尋找替他解決的辦法, 是AnimalTrianer.act()方法變得更爲通用(既能夠接受List<Animal>類型,也能夠接受List<Cat>等參數)。在java裏解決辦法就是使用通配符「?」,具體到AnimalTrianer,就是將方法改成act(List<? extends Animal> list),當中「?」就是通配符,而「? extends Animal」則表示通配符「?」的上界爲Animal,換句話說就是,「? extends Animal」能夠表明Animal或其子類,可表明不了Animal的父類(如Object),由於通配符的上界是Animal。以下,爲改進以後的AnimalTrianer數組
public class AnimalTrainer { public void act(List<? extends Animal> list) { for (Animal animal : list) { animal.eat(); } } }
再來測試一下,以下,發現Test 2 能夠經過編譯了:ide
public class TestAnimal { public static void main(String[] args) { AnimalTrainer animalTrainer = new AnimalTrainer(); //Test 1 List<Animal> animalList = new ArrayList<>(); animalList.add(new Cat("cat1")); animalList.add(new Bird("bird1")); animalTrainer.act(animalList); //能夠經過編譯 //Test 2 List<Cat> catList = new ArrayList<>(); catList.add(new Cat("cat2")); catList.add(new Cat("cat3")); animalTrainer.act(catList); //也能夠經過編譯 } }
通過上述分析,能夠知道List<Animal>和List<Cat>都是List<? extends Animal>的子類型,相似有List<Bird>,List<Magpie>也是List<? extends Animal>的子類型。測試
現總結以下,對於通配符的上界,有如下幾條基本規則:(假設給定的泛型類型爲G,(如List<E>中的List),兩個具體的泛型參數X、Y,當中Y是X的子類(如上的Animal和Cat))this
學到這裏,可能會遇到一些疑惑的地方,或者說事理解不透的地方,先觀察以下兩段代碼片斷,判斷一下其是否可行??設計
public void testAdd(List<? extends Animal> list){ //....其餘邏輯 list.add(new Animal("animal")); list.add(new Bird("bird")); list.add(new Cat("cat")); }
List<? extends Animal> list = new ArrayList<>(); list.add(new Animal("animal")); list.add(new Bird("bird")); list.add(new Cat("cat"));
先分析以下:由於「? extends Animal」可表明Animal或其子類(Bird,Cat),那上面的操做應該是可行的。事實上是」不行「,即沒法經過編譯。爲何呢??htm
在解釋以前,再來從新強調一下已經知道的規則:在List<Aimal> list裏只能添加Animal類對象及其子類對象(如Cat和Bird對象),在List<Bird>裏只能添加Bird類和其子類對象(如Magpie),可不能添加Animal對象(不是Bird的子類),相似的在List<Cat>和List<Magpie>裏只能添加Cat和Bird對象(或其子類對象,不過這沒有列出)。如今再回頭看一下testAdd()方法,咱們知道List<Animal>、List<Cat等都是List<? extends Animal>的子類型。先假設傳入的參數爲爲List<Animal>,則第一段代碼的三個「add」操做都是可行的;可若是是List<Bird>呢??則只有第二個「add」能夠執行;再假設傳入的是List<Tiger>(Tiger是想象出來的,可認爲是Cat的子類),則三個「add」操做都不能執行。對象
如今反過來講,給testAdd傳入不一樣的參數,三個「add」操做均可能引起類型不兼容問題,而傳入的參數是未知的,因此java爲了保護其類型一致,禁止向List<? extends Animal>添加任意對象,不過卻能夠添加null,即list.add(null)是可行的。有了上面談到的基礎,再來理解第二段代碼就不難了,由於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(); }
既然有了通配符的上界,天然有着通配符的下界。能夠如此定義通配符的下界 List<? super Bird>,其中」Bird「就是通配符的下界。注意:不能同時聲明泛型通配符申明上界和下界。
在談注意細節以前,咱們先看一下通配符的使用規則——對於通配符的上界,有如下幾條基本規則:(假設給定的泛型類型爲G,(如List<E>中的List),兩個具體的泛型參數X、Y,當中Y是X的子類(如上的Animal和Cat))
如今再來看以下代碼,判斷其是否符合邏輯:
public void testAdd(List<? super Bird> list){ list.add(new Bird("bird")); list.add(new Magpie("magpie")); }
List<? super Bird> list = new ArrayList<>(); list.add(new Bird("bird")); list.add(new Magpie("magpie")); list.add(new Animal("animal"));
看第一段代碼,其分析以下,由於」? super Bird」表明了Bird或其父類,而Magpie是Bird的子類,因此上訴代碼不可經過編譯。而事實上是」行「,爲何呢?2?
在解疑以前,再來強調一個知識點,子類能夠指向父類,即Bird也是Animal對象。如今考慮傳入到testAdd()的全部可能的參數,能夠是List<Bird>,List<Animal>,或者List<Objext>等等,發現這些參數的類型是Bird或其父類,那咱們能夠這樣看,把bird、magpie當作Bird對象,也能夠將bird、magpie當作Animal對象,相似的可當作Object對象,最後發現這些添加到List<? supe Bird> list裏的對象都是同一類對象(如本文剛開篇提到的Test 1),所以testAdd方法是符合邏輯,能夠經過編譯的。:
如今再來看一下第二段代碼對於,第2、三行代碼的解釋和上文同樣,至於最後一行「list.add(newAnimal("animal"))」是沒法經過編譯的,爲何的??爲了保護類型的一致性,由於「? super Bird」能夠是Animal,也能夠是Object或其餘Bird的父類,因沒法肯定其類型,也就不能往List<? super Bird>添加Bird的任意父類對象。
既然沒法肯定其父類對象,那該如何遍歷List<? super Bird> ? 由於Object是全部類的根類,因此能夠用Object來遍歷。以下,不過貌似其意義不大。
for (Object object : list) {//...}
那「? super BoundingType」能夠應用在什麼地方呢??「? super BoundingType」應用相對普遍,只不過是混合着用。下面舉個簡單的例子。先假設有如下兩個Student和CollegeStudent,當中CollegeStudent繼承Student,以下:
public class Student implements Comparable<Student>{ private int id; public Student(int id) { this.id = id; } @Override public int compareTo(Student o) { return (id > o.id) ? 1 : ((id < o.id) ? -1 : 0); } }
public class CollegeStudent extends Student{ public CollegeStudent(int id) { super(id); } }
先須要根據他們的id對他們進行排序(注意此處是對數組對象進行排序),設計方法以下,(n指數組元素的個數):
public static <T extends Comparable<? super T>> void selectionSort(T[] a,int n)
先理解此方法含義,首先<T extends Comparable<T>>規定了數組中對象必須實現Comparable接口,Comparable<? Super T>表示若是父類實現Comparable接口,其自身可不實現,如CollegeStudent。先假設有一個CollegeStudent的數組,以下:
CollegeStudent[] stu = new CollegeStudent[]{ new CollegeStudent(3),new CollegeStudent(2), new CollegeStudent(5),new CollegeStudent(4)};
執行方法 selectionSort(stu,4)是徹底能夠經過的。可若是定義的selectionSort方法以下:
public static <T extends Comparable<T>> void selectionSort(T[] a,int n)
則方法selectionSort(stu,4)不能執行,由於CollegeStudent沒有實現Comparable<CollegeStudent>接口。換句話就是「? super T」使selectionSort方法變得更爲通用了。selectionSort完整代碼的實現可參考本文的末尾。
知道了通配符的上界和下界,其實也等同於知道了無界通配符,不加任何修飾便可,單獨一個「?」。如List<?>,「?」能夠表明任意類型,「任意」也就是未知類型。無界通配符一般會用在下面兩種狀況:
一、當方法是使用原始的Object類型做爲參數時,以下:
public static void printList(List<Object> list) { for (Object elem : list) System.out.println(elem + ""); System.out.println(); }
能夠選擇改成以下實現:
public static void printList(List<?> list) { for (Object elem: list) System.out.print(elem + ""); System.out.println(); }
這樣就能夠兼容更多的輸出,而不單純是List<Object>,以下:
List<Integer> li = Arrays.asList(1, 2, 3); List<String> ls = Arrays.asList("one", "two", "three"); printList(li); printList(ls);
二、在定義的方法體的業務邏輯與泛型類型無關,如List.size,List.cleat。實際上,最經常使用的就是Class<?>,由於Class<T>並無依賴於T。
最後提醒一下的就是,List<Object>與List<?>並不等同,List<Object>是List<?>的子類。還有不能往List<?> list裏添加任意對象,除了null。
附錄:selectionSort的代碼實現:(若是須要實現比較好的輸出,最好重寫Student的toString方法)
public class SortArray { //對一組數組對象運用插入排序,n指數組元素的個數 public static <T extends Comparable<? super T>> void selectionSort(T[] a,int n) { for (int index = 0; index < n-1; index++) { int indexOfSmallest = getIndexOfSmallest(a,index,n-1); swap(a,index,indexOfSmallest); } } public static <T extends Comparable<? super T>> int getIndexOfSmallest(T[] a, int first, int last) { T minValue = a[first]; // 假設第一個爲minValue int indexOfMin = first; // 取得minValue的下標 for (int index = first + 1; index <= last; index++) { if (a[index].compareTo(minValue) < 0) { minValue = a[index]; indexOfMin = index; } } return indexOfMin; } public static void swap(Object[] a,int first,int second) { Object temp = a[first]; a[first] = a[second]; a[second] = temp; } public static void main(String[] args) { CollegeStudent[] stu = new CollegeStudent[]{ new CollegeStudent(3), new CollegeStudent(2), new CollegeStudent(5), new CollegeStudent(4)}; selectionSort(stu, 4); for (Student student : stu) { System.out.println(student); } } }