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

猜你喜欢

转载自blog.csdn.net/qq_36952611/article/details/73504040