目录

一、通配符类型是什么?

二、通配符类型怎么用?

<1> 通配符的分类

1) 通配符的子类限定

2) 通配符的父类限定

三、总结


今天我们来聊一聊Java中的通配符~

由于严格的泛型类型让程序员使用起来并不是那么方便,Java设计者于是就设计了一种巧妙并且安全的解决方案——通配符类型( ? )

一、通配符类型是什么?

通配符类型,允许参数发生变化,用 ? 表示

? : 表示不确定的 Java 类型,是通配符类型,代表所有类型且不会进行类型推断

二、通配符类型怎么用?

<1> 通配符的分类

① 通配符的子类型限定:? extends 父类( 超类 )

② 通配符的超类型限定:? super 子类( 基类 )

③ 无限定通配符:类名<?>

我们先谈谈①和②,当我们看到名称之后,就应该会想到这应该跟子类和父类息息相关,所以我们这里创建一下子类和父类。当然,肯定也少不了泛型类。

Ⅰ、Person类

public class Person {
    private String name;
    private int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}

Ⅱ、Student类

public class Student extends Person{
    private String grade;

    public Student(String name, int age, String grade) {
        super(name, age);
        this.grade = grade;
    }

    public String getGrade() {
        return grade;
    }

    public void setGrade(String grade) {
        this.grade = grade;
    }
}

Ⅲ、MiddleStudent类

public class MiddleStudent extends Student{
    private int rollNumber;

    public MiddleStudent(String name, int age, String grade, int rollNumber) {
        super(name, age, grade);
        this.rollNumber = rollNumber;
    }

    public int getRollNumber() {
        return rollNumber;
    }

    public void setRollNumber(int rollNumber) {
        this.rollNumber = rollNumber;
    }
}

创建完成子类和父类后,我们自定义一个简单泛型类——Pair类

public class Pair<T>{
    private T first;
    private T second;

    public Pair() {
        first = null;
        second = null;
    }

    public Pair(T first, T second) {
        this.first = first;
        this.second = second;
    }

    public T getFirst() {
        return first;
    }

    public void setFirst(T first) {
        this.first = first;
    }

    public T getSecond() {
        return second;
    }

    public void setSecond(T second) {
        this.second = second;
    }
}

接下来,我们就定义方法去测试通配符的子类限定和通配符的父类限定,看看这两者有着什么不同?

1) 通配符的子类限定

测试样例—get方法

public class Test {
    public static void main(String[] args) {
        Student s1 = new Student("子类1", 18, "小学");
        Student s2 = new Student("子类2", 19, "初中");
        Pair<Student> studentPair = new Pair<>(s1, s2);
        toDoTest01(studentPair);
    }

    public static void toDoTest01(Pair<? extends Person> p) {
        Person s1 = p.getFirst();
        Person s2 = p.getSecond();
        System.out.println("Student1: " + s1 + ", Student1-Name: " + s1.getName() + ", Student1-Age: " + s1.getAge());
        System.out.println("Student2: " + s2 + ", Student2-Name: " + s2.getName() + ", Student2-Age: " + s2.getAge());
    }
}

测试结果—get方法

Student1: Test.Demo01.Student@4554617c, Student1-Name: 子类1, Student1-Age: 18
Student2: Test.Demo01.Student@74a14482, Student2-Name: 子类2, Student2-Age: 19

说明:通配符的子类限定时,编译器只需要知道是父类的某个子类型,但是并不知道具体是什么类型,将getFirst的返回值赋值给父类引用,是没有问题的!简单来说就是带有子类型限定的通配符允许我们读取一个泛型对象。

测试样例—set方法

public class Test {
    public static void main(String[] args) {
        Student s1 = new Student("子类1", 18, "小学");
        Student s2 = new Student("子类2", 19, "初中");
        Pair<Student> studentPair = new Pair<>(s1, s2);
        toDoTest01(studentPair);
    }

    public static void toDoTest01(Pair<? extends Person> p) {
         Student student = new Student("张三", 18, "高中");
         Person s = p.setFirst(student);
    }
}

java 通配符中使用方法 java中通配符作用_intellij-idea

报错的原因: 是因为编译器只需要知道是父类的某个子类型,但是并不知道具体是什么类型,所以编译器拒绝传递任何特定的类型参数。

2) 通配符的父类限定

测试样例—get方法

public class Test {
    public static void main(String[] args) {
        Person p1 = new Person("父类1", 30);
        Person p2 = new Person("父类2", 31);
        Pair<Person> personPair = new Pair<>(p1, p2);
        toDoTest02(personPair);
    }

    public static void toDoTest02(Pair<? super Student> s) {
        System.out.println("s: " + s);
        Object p1 = s.getFirst();
        Object p2 = s.getSecond();
        System.out.println("p1: " + p1);
        System.out.println("p2: " + p2);
    }
}

测试结果—get方法

s: Test.Demo01.Pair@4554617c
p1: Test.Demo01.Person@74a14482
p2: Test.Demo01.Person@1540e19d

java 通配符中使用方法 java中通配符作用_intellij-idea_02

并没有 getName( ),getAge( ) 方法,由于不能保证返回类型,所以只能将返回的类型赋值为Object 类型的对象变量,无法调用get方法。

测试样例—set方法

public class Test {
    public static void main(String[] args) {
        Person p1 = new Person("父类1", 30);
        Person p2 = new Person("父类2", 31);
        Pair<Person> personPair = new Pair<>(p1, p2);
        toDoTest02(personPair);
    }

    public static void toDoTest02(Pair<? super Student> s) {
        Student student = new Student("张三", 18, "高中");
        MiddleStudent middleStudent = new MiddleStudent("小明", 10, "小学", 1001);
        // Person person = new Person("李四", 30);
        s.setFirst(student);
        s.setSecond(middleStudent);
        // s.setFirst(person);
    }
}

java 通配符中使用方法 java中通配符作用_java 通配符中使用方法_03

由于编译器无法知道 setFirst( ) 方法的具体类型,所以不能接受参数类型父类—Person类的调用,只能传递参数为子类—Student类,或者某个子类型对象—MiddleStudent类

三、总结

简单来说,带有超类型限定的通配符允许你写入一个泛型对象,而带有子类限定的通配符允许你读取一个泛型对象。

这里我们在 ③无限定通配符 不做过多赘述,例如:Pair<?>

类型 参数类型 Pair<?> 只能调用getFirst方法,getFirst( )方法的返回值也只能赋值给Object,并且setFirst( )方法不能被调用,甚至不能用Object方法调用。