当我们声明一个泛型类变量时,编译器会用具体的类型代替泛型类的类型变量。
比如,我们有下面这些类:
package generic;
/**
* @version 1.00 2004-05-10
* @author Cay Horstmann
*/
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 T getSecond() { return second; }
public void setFirst(T newValue) { first = newValue; }
public void setSecond(T newValue) { second = newValue; }
}
package generic;
public class Person{
private String name;
public Person(String name) {
super();
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Person [name=" + name + "]";
}
public Integer printAndReturn() {
return 1;
}
}
package generic;
public class Student extends Person {
private String studentNumber;
public Student(String name, String studentNumber) {
super(name);
this.studentNumber = studentNumber;
}
public String getStudentNumber() {
return studentNumber;
}
public void setStudentNumber(String studentNumber) {
this.studentNumber = studentNumber;
}
@Override
public String toString() {
return "Student [studentNumber=" + studentNumber + ", name="
+ getName() + "]";
}
}
我们有如下声明:
Pair<Person> personPair;
它只能引用Pair<Person>类型的的实例:
personPair = new Pair<Person>(new Person("li"), new Person("wang")); //ok
却不能引用Pair<Student>类型的实例:
personPair = new Pair<Student>(new Student("li", "1"), new Student("wang", "2")); //error
因为Pair<Student>不是Pair<Person>,也不是它的子类。
有时候,我们可能希望一个变量既能引用Pair<Person>类型的的实例,也能引用Pair<Student>类型的实例,该怎么办呢?这就需要用到通配符了。
1.通配符的子类型限定
下面的变量声明:
Pair<? extends Person> personPair;
personPair既能引用Pair<Person>类型的的实例,也能引用Pair<Student>类型的实例。因为Pair<? extends Person>表示任何类型参数是Person或其子类型的Pair类型。对personPair进行如下赋值:
personPair = new Pair<>(new Student("li", "1"),new Student("wang", "2"));// ok
然后,我们想修改personPair的第一个值为new Student("yuncong", "3"):
/**
* error,
* setFirst方法是这个样子的:
* void setFirst(? extends Person);
* 可以向方法中传入指定的参数类型或其子类型,
* 这个地方只知道指定的参数类型是Person的子类,
* 不知道具体的指定类型是什么,也就
* 无法断定传入哪种类型的参数是对的,比如Person
* 有子类Student,Student有子类SeniorStudent,
* 如果指定参数类型是SeniorStudent,我们就不能把Student
* 传进去;因此无法调用这个方法
*/
personPair.setFirst(new Student("yuncong", "3")); //error
我们不能向personPair中传入值,原因已经在注释中解释了。
但是我们可以从personPair中取出值,原因也在注释中给出:
/**
* ok,
* getFirst方法是这个样子的:
* ? extends Person getFirst(),
* 它返回的是Person的子类,把Person的子类
* 的实例赋值给Person类变量完全没有问题
*/
Person person = pair.getFirst(); //ok
可见,只可以从|类型变量替换为带有子类型限定的通配符的|(竖线之间的是一个长长的修饰语)泛型类对象中读取。
2.通配符的超类型限定
通配符的超类型限定提供了与通配符的子类型限定相反的行为,只可以向类型变量替换为带有超类型限定的通配符的泛型类对象中写入。
假设我们声明了这样一个personPair:
/**
* Pair<? super Student>表示参数类型为Student
* 或其父类的Pair类型;
* 父类型变量可以引用子类型实例
*/
Pair<? super Student> personPair = new Pair<Student>(new Student("li", "1"),
new Student("wang", "2"));
可以设置personPair的值:
/**
* ok,
* setFirst方法是这个样子的:
* void setFirst(? super Student);
* 因为一个方法能接收它指定参数类型及子类类型的参数,
* 这里只知道传入参数是Student的父类,虽然不知道具体的父类
* 是什么,但是传入Student类及其子类的实例必定属于Student的父类
* 的子类
*/
personPair.setFirst(new Student("wang", "2"));//ok
但不能从personPair中读取值(也不是完全不可以,只是得到的实例的类型得不到保证):
/**
* getFirst方法是这个样子的:
* ? super Student getFirst(),
* 只知道它的返回类型必定是Student及其父类的实例,
* 但必定是Object或它的子类
*/
Object object = personPair.getFirst();