在Java中的泛型详解(一)这篇博客中,我详细讲解了一下泛型的概念,擦除机制,泛型的上界以及泛型方法Java中的泛型详解(一),这篇博客将着重讲解一下泛型中另一个较为重要的知识——通配符,我们学习泛型的主要目的是为了后期学习数据结构的时候看懂源码,能够了解每一种数据结构背后是如何实现的从而深入掌握数据结构

什么是通配符

**?**在泛型中就是一种通配符的符合,在泛型类传参的时候,传入的参数是什么类型我们就只能对这种数据类型进行操作,而通配符一般说明传入的参数是不确定的,这样操作起来的时候会显得更加灵活。

分类

通配符和泛型不同,泛型只有上界,而通配符既有上界也有下界,每一种对应的语法不同且使用的场景也有所区别;下面我们将用代码来分别介绍他们的区别。

Java泛型 通配符 java通配符和泛型_数据结构

代码如下:

在这里插入代码片class Food {

}
class Fruit extends Food{

}
class Apple extends Fruit {

}
class Banana extends Fruit {

}
class Plate<T> {
    public T plate;
    public T getPlate() {
        return plate;
    }
    public void setPlate(T plate) {
        this.plate = plate;
    }
}

其中Plate是一个泛型类,类中有两个方法,一个是设置成员变量,一个是获取成员变量。

通配符的上界

语法

这里是引用<? extends 上界>
这里的通配符?表示传入进来的类必须是上界本身或者上界的子类

public class Test {
    public static void main(String[] args) {
        Plate<Apple> plate = new Plate<>();
        plate.setPlate(new Apple());
        fun(plate);
        
        Plate<Banana> plate1 = new Plate<>();
        plate1.setPlate(new Banana());
        fun(plate1);
        
        Plate<Food> plate2 = new Plate<>();
        fun(plate2);
    }
    public static void fun(Plate<? extends Fruit> plate) {
        plate.setPlate(new Apple());
        plate.setPlate(new Banana());
        plate.setPlate(new Fruit());
        
        Fruit fruit = plate.getPlate();
    }
}

该代码的主要功能是在main方法里面调用fun方法,fun方法中的参数是一个通配符的上界,然后进行修改数据和读取数据的操作

Java泛型 通配符 java通配符和泛型_数据结构_02


注意:

  1. fun方法中通配符的上界是Fruit,而Food是Fruit的父类,所以fun方法不能接受Food类型的变量,故注释1处报错;
  2. fun方法接受的类型都是Fruit本身或者Fruit的子类,此时传入的类型并不能确定,用fun方法中的plate对象调用setPlate方法进行修改数据并不知道类型,故注释2处报错;
  3. 虽然不知道fun方法接受的类型,但是知道他是Fruit本身或者Fruit的子类,所以可以使用plate对象调用getPlate方法来获取对象,获取的对象用Fruit定义的变量接受(这里相当于向上转型),故注释3处编译通过;
    总结:通配符的上界不能用来写入数据,但是可以用来读取数据

通配符的下界

语法

这里是引用<? super 下界>
这里的通配符?表示传入进来的类必须是下界本身或者下界的父类

public class Test {
    public static void main(String[] args) {
        Plate<Fruit> plate = new Plate<>();
        plate.setPlate(new Fruit());
        fun(plate);

        Plate<Food> plate1 = new Plate<>();
        fun(plate1);
        
        Plate<Apple> plate2 = new Plate<>();
        fun(plate2);
    }
    public static void fun(Plate<? super Fruit> plate) {
        plate.setPlate(new Apple());
        plate.setPlate(new Banana());
        plate.setPlate(new Fruit());
        plate.setPlate(new Food());

        Fruit fruit = plate.getPlate();
    }
}

该代码的主要功能是在main方法里面调用fun方法,fun方法中的参数是一个通配符的上界,然后进行修改数据和读取数据的操作

Java泛型 通配符 java通配符和泛型_数据结构_03


注意:

  1. fun方法中通配符的下界是Fruit,只能接受Fruit和Fruit的父类的类型,Apple是其子类,故注释1处报错;
  2. fun方法接受的都是Fruit或者其父类,plate对象调用setPlate方法时可写入Fruit及其子类类型的数据,故注释2处前三行代码不报错,但new Food()时报错;
  3. fun方法接受的都是Fruit或者其父类,类型不能确定,不能使用Fruit类型接受setPlate方法所返回的值;
    总结:通配符的下界不能用来读取数据,但是可以用来写入数据(写入时只能写入通配符本身或者其子类)