目录
一、通配符类型是什么?
二、通配符类型怎么用?
<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);
}
}
报错的原因: 是因为编译器只需要知道是父类的某个子类型,但是并不知道具体是什么类型,所以编译器拒绝传递任何特定的类型参数。
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
并没有 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);
}
}
由于编译器无法知道 setFirst( ) 方法的具体类型,所以不能接受参数类型父类—Person类的调用,只能传递参数为子类—Student类,或者某个子类型对象—MiddleStudent类
三、总结
简单来说,带有超类型限定的通配符允许你写入一个泛型对象,而带有子类限定的通配符允许你读取一个泛型对象。
这里我们在 ③无限定通配符 不做过多赘述,例如:Pair<?>
类型 参数类型 Pair<?> 只能调用getFirst方法,getFirst( )方法的返回值也只能赋值给Object,并且setFirst( )方法不能被调用,甚至不能用Object方法调用。