1. 程式人生 > >java泛型中的萬用字元?問題

java泛型中的萬用字元?問題

本文是經過網上查詢的資料整合而來,給自己做個筆記,也分享給大家!需要你對泛型有一定的基礎瞭解。
package Test8_8;

import java.util.ArrayList;
import java.util.List;

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; } } class Cat extends Animal { public Cat(String name) { super(name); } public void jump(){ System.out.println(getName() + " can jump."); } } class Bird extends Animal { public Bird(String name) { super
(name); } public void fly(){ System.out.println(getName() + " can fly."); } } class Magpie extends Bird { public Magpie(String name) { super(name); } public void sing(){ System.out.println(getName() + " can not only eat,but sing"); } } //首先我們看一下無萬用字元的使用示例,如下:
/*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<>(); //這裡的cat是作為animal的子類新增進去的 animalList.add(new Cat("cat1")); animalList.add(new Bird("bird1")); animalTrainer.act(animalList); //可以通過編譯 //Test 2 List<Cat> catList = new ArrayList<>(); //這裡的cat是作為單獨的新增的List<Cat>裡面 catList.add(new Cat("cat2")); catList.add(new Cat("cat3")); animalTrainer.act(catList); //無法通過編譯,List<cat>不是List<animal>的子類 } }

萬用字元上界

我們必須要清楚的認識到雖然Cat是Animal的子類,但是List並不是List的子類。那麼我們就要尋找替代方法解決使得AnimalTrianer.act()方法變得更為通用(即不僅支援List型別,又能支援List型別),那麼就需要將其方法引數型別改為

//改進後使編譯可以通過
class AnimalTrainer {
    public void act(List<? extends Animal> list) {
        for (Animal animal : list) {
            animal.eat();
        }
    }
}

萬用字元上界有以下幾條規則:
1. List

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

我們知道List

public void testAdd(List<? super Bird> list){
        list.add(new Bird("bird"));
        list.add(new Magpie("magpie"));
        list.add((Bird) new Animal("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方法是符合邏輯,可以通過編譯的。:
現在再來看一下第二段程式碼對於,第二、三行程式碼的解釋和上文一樣,至於最後一行“list.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);
    }
}

先需要根據他們的id對他們進行排序(注意此處是對陣列物件進行排序),設計方法如下,(n指陣列元素的個數):

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

先理解此方法含義,首先

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介面。換句話就是“? super T”使selectionSort方法變得更為通用了。selectionSort完整程式碼的實現可參考本文的末尾。

無界萬用字元

   知道了萬用字元的上界和下界,其實也等同於知道了無界萬用字元,不加任何修飾即可,單獨一個“?”。如List<?>,“?”可以代表任意型別,“任意”也就是未知型別。無界萬用字元通常會用在下面兩種情況:

1、當方法是使用原始的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,如下:

List<Integer> li = Arrays.asList(1, 2, 3);
List<String>  ls = Arrays.asList("one", "two", "three");
printList(li);
printList(ls);

2、在定義的方法體的業務邏輯與泛型型別無關,如List.size,List.cleat。實際上,最常用的就是Class