乡下人产国偷v产偷v自拍,国产午夜片在线观看,婷婷成人亚洲综合国产麻豆,久久综合给合久久狠狠狠9

  • <output id="e9wm2"></output>
    <s id="e9wm2"><nobr id="e9wm2"><ins id="e9wm2"></ins></nobr></s>

    • 分享

      java 通配符解惑

       一本正經地胡鬧 2019-07-13
      Technorati 標記: 通配符,wildcard,java

          本以為這會是一篇比較基礎的博客,可一旦深究的時候,才發(fā)現(xiàn)很多有意思的東西,也發(fā)現(xiàn)了很多令人迷惑的地方。通配符是一個有趣的東西,如果你掌握了,會使你的代碼更為通用(健壯性更強)。首先本文是在建立在java泛型基礎之上的,如果你對泛型并不了解,可以點擊 這里。同時為了對通配符的了解更為透切,定義如下幾個類。

      public class Animal {	private String name;	public Animal(String name) {		this.name = name;
      	}	
      	public void eat() {
      		System.out.println(getName() + " can eat.");
      	}	
      	public String getName(){		return name;
      	}
      }
      public class Cat extends Animal {	public Cat(String name) {		super(name);
      	}	public void jump(){
      		System.out.println(getName() + " can jump.");
      	}
      }
      public class Bird extends Animal {	public Bird(String name) {		super(name);
      	}	public void fly(){
      		System.out.println(getName() + " can fly.");
      	}
      }
      public class Magpie extends Bird {	public Magpie(String name) {		super(name);
      	}	public void sing(){
      		System.out.println(getName() + 
      				" can not only eat,but sing");
      	}
      }


          首先我們看一下無通配符的使用示例,如下:

      public class AnimalTrainer {	public void act(List<Animal> list) {		for (Animal animal : list) {
      			animal.eat();
      		}
      	}
      }

           測試代碼如下:

      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);		//無法通過編譯
      	}
      }

          如上,Test 1 的執(zhí)行應該可以理解的,不過順帶提醒一下的是,因為cat1和bird1都是Animal對象,自然可以添加List<Animal>里,具體解釋可參考 java泛型基礎 。對于Test 2,無法通過編譯是因為List<Cat>并不是List<Animal>子類,傳入?yún)?shù)有誤,也就無法通過編譯了?,F(xiàn)在嘗試去修改AnimalTrainer.act()代碼,讓它變得更為通用的一點,即不僅僅是接受List<Animal>參數(shù),還可以接受List<Bird>等參數(shù)。那如何更改呢??

      一、通配符的上界

          既然知道List<Cat>并不是List<Anilmal>的子類型,那就需要去尋找替他解決的辦法, 是AnimalTrianer.act()方法變得更為通用(既可以接受List<Animal>類型,也可以接受List<Cat>等參數(shù))。在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();
      		}
      	}
      }

          再來測試一下,如下,發(fā)現(xiàn)Test 2 可以通過編譯了:

      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>的子類型?,F(xiàn)總結如下,對于通配符的上界,有以下幾條基本規(guī)則:(假設給定的泛型類型為G,(如List<E>中的List),兩個具體的泛型參數(shù)X、Y,當中Y是X的子類(如上的Animal和Cat))

      • G<? extends Y> 是 G<? extends X>的子類型(如List<? extends Cat> 是 List<? extends Animal>的子類型)。

      • G<X> 是 G<? extends X>的子類型(如List<Animal> 是 List<? extends Animal>的子類型)

      • G<?> 與 G<? extends Object>等同,如List<?> 與List<? extends Objext>等同。

          學到這里,可能會遇到一些疑惑的地方,或者說事理解不透的地方,先觀察如下兩段代碼片段,判斷一下其是否可行??

      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),那上面的操作應該是可行的。事實上是”不行“,即無法通過編譯。為什么呢??

           在解釋之前,再來重新強調一下已經知道的規(guī)則:在List<Aimal> list里只能添加Animal類對象及其子類對象(如Cat和Bird對象),在List<Bird>里只能添加Bird類和其子類對象(如Magpie),可不能添加Animal對象(不是Bird的子類),類似的在List<Cat>和List<Magpie>里只能添加Cat和Bird對象(或其子類對象,不過這沒有列出)。現(xiàn)在再回頭看一下testAdd()方法,我們知道List<Animal>、List<Cat等都是List<? extends Animal>的子類型。先假設傳入的參數(shù)為為List<Animal>,則第一段代碼的三個“add”操作都是可行的;可如果是List<Bird>呢??則只有第二個“add”可以執(zhí)行;再假設傳入的是List<Tiger>(Tiger是想象出來的,可認為是Cat的子類),則三個“add”操作都不能執(zhí)行。

           現(xiàn)在反過來說,給testAdd傳入不同的參數(shù),三個“add”操作都可能引發(fā)類型不兼容問題,而傳入的參數(shù)是未知的,所以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(); }

      二、通配符的下界

          既然有了通配符的上界,自然有著通配符的下界??梢匀绱硕x通配符的下界 List<? super Bird>,其中”Bird“就是通配符的下界。注意:不能同時聲明泛型通配符申明上界和下界。

          在談注意細節(jié)之前,我們先看一下通配符的使用規(guī)則——對于通配符的上界,有以下幾條基本規(guī)則:(假設給定的泛型類型為G,(如List<E>中的List),兩個具體的泛型參數(shù)X、Y,當中Y是X的子類(如上的Animal和Cat))

      • G<? super X> 是 G<? super Y>的子類型(如List<? super Animal> 是 List<? super Bird>的子類型)。

      • G<X> 是 G<? super X>的子類型(如List<Animal> 是 List<? super Animal>的子類型)

         現(xiàn)在再來看如下代碼,判斷其是否符合邏輯:

      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對象?,F(xiàn)在考慮傳入到testAdd()的所有可能的參數(shù),可以是List<Bird>,List<Animal>,或者List<Objext>等等,發(fā)現(xiàn)這些參數(shù)的類型是Bird或其父類,那我們可以這樣看,把bird、magpie看成Bird對象,也可以將bird、magpie看成Animal對象,類似的可看成Object對象,最后發(fā)現(xiàn)這些添加到List<? supe Bird> list里的對象都是同一類對象(如本文剛開篇提到的Test 1),因此testAdd方法是符合邏輯,可以通過編譯的。:

           現(xiàn)在再來看一下第二段代碼對于,第二、三行代碼的解釋和上文一樣,至于最后一行“l(fā)ist.add(new Animal("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);
      	}
      }


          先需要根據(jù)他們的id對他們進行排序(注意此處是對數(shù)組對象進行排序),設計方法如下,(n指數(shù)組元素的個數(shù)):

      public static <T extends Comparable<? super T>> 
      			void selectionSort(T[] a,int n)

          先理解此方法含義,首先<T extends Comparable<T>>規(guī)定了數(shù)組中對象必須實現(xiàn)Comparable接口,Comparable<? Super T>表示如果父類實現(xiàn)Comparable接口,其自身可不實現(xiàn),如CollegeStudent。先假設有一個CollegeStudent的數(shù)組,如下:

      CollegeStudent[] stu = new CollegeStudent[]{   new CollegeStudent(3),new CollegeStudent(2),   new CollegeStudent(5),new CollegeStudent(4)};


          執(zhí)行方法 selectionSort(stu,4)是完全可以通過的。可如果定義的selectionSort方法如下:

      public static <T extends Comparable<T>> 
      			void selectionSort(T[] a,int n)

          則方法selectionSort(stu,4)不能執(zhí)行,因為CollegeStudent沒有實現(xiàn)Comparable<CollegeStudent>接口。換句話就是“? super T”使selectionSort方法變得更為通用了。selectionSort完整代碼的實現(xiàn)可參考本文的末尾。

      三、×××通配符

          知道了通配符的上界和下界,其實也等同于知道了×××通配符,不加任何修飾即可,單獨一個“?”。如List<?>,“?”可以代表任意類型,“任意”也就是未知類型?!痢痢镣ㄅ浞ǔ迷谙旅鎯煞N情況:1、當方法是使用原始的Object類型作為參數(shù)時,如下:

      public static void printList(List<Object> list) {for (Object elem : list)
              System.out.println(elem + " ");
          System.out.println();
      }

          可以選擇改為如下實現(xiàn):

      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);

          2、在定義的方法體的業(yè)務邏輯與泛型類型無關,如List.size,List.cleat。實際上,最常用的就是Class<?>,因為Class<T>并沒有依賴于T。

          最后提醒一下的就是,List<Object>與List<?>并不等同,List<Object>是List<?>的子類。還有不能往List<?> list里添加任意對象,除了null。

      附錄:selectionSort的代碼實現(xiàn):(如果需要實現(xiàn)比較好的輸出,最好重寫Student的toString方法)

      public class SortArray {	
      	//對一組數(shù)組對象運用插入排序,n指數(shù)組元素的個數(shù)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]; // 假設第一個為minValueint 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);
      		}
      	}
      }

        本站是提供個人知識管理的網(wǎng)絡存儲空間,所有內容均由用戶發(fā)布,不代表本站觀點。請注意甄別內容中的聯(lián)系方式、誘導購買等信息,謹防詐騙。如發(fā)現(xiàn)有害或侵權內容,請點擊一鍵舉報。
        轉藏 分享 獻花(0

        0條評論

        發(fā)表

        請遵守用戶 評論公約

        類似文章 更多