原文地址:http://blog.csdn.net/fw0124/article/details/42296283
泛型中使用通配符有两种形式:子类型限定<? extends xxx>和超类型限定<? super xxx>。
(1)子类型限定
下面的代码定义了一个Pair<T>类,以及Employee,Manager和President类。
- public class Pair<T> {
- private T first;
- private T second;
- 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;
- }
- }
- class Employee {
- private String name;
- private double salary;
- public Employee(String n, double s) {
- name = n;
- salary = s;
- }
- public String getName() {
- return name;
- }
- public double getSalary() {
- return salary;
- }
- }
- class Manager extends Employee {
- public Manager(String n, double s) {
- super(n, s);
- }
- }
- <pre name="code" class="java">
- class President extends Manager {
- public President(String n, double s) {
- super(n, s);
- }
- }
public class Pair<T> { private T first; private T second; 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; } } class Employee { private String name; private double salary; public Employee(String n, double s) { name = n; salary = s; } public String getName() { return name; } public double getSalary() { return salary; } } class Manager extends Employee { public Manager(String n, double s) { super(n, s); } } <pre name="code" class="java"> class President extends Manager { public President(String n, double s) { super(n, s); } }
现在要定义一个函数可以打印Pair<Employee>
- public static void printEmployeeBoddies(Pair<Employee> pair) {
- System.out.println(pair.getFirst().getName() + ":" + pair.getSecond().getName());
- }
public static void printEmployeeBoddies(Pair<Employee> pair) { System.out.println(pair.getFirst().getName() + ":" + pair.getSecond().getName()); }
可是有一个问题是这个函数输入参数只能传递类型Pair<Employee>,而不能传递Pair<Manager>和Pair<President>。例如下面的代码会产生编译错误
- Manager mgr1 = new Manager("Jack", 10000.99);
- Manager mgr2 = new Manager("Tom", 10001.01);
- Pair<Manager> managerPair = new Pair<Manager>(mgr1, mgr2);
- PairAlg.printEmployeeBoddies(managerPair);
Manager mgr1 = new Manager("Jack", 10000.99); Manager mgr2 = new Manager("Tom", 10001.01); Pair<Manager> managerPair = new Pair<Manager>(mgr1, mgr2); PairAlg.printEmployeeBoddies(managerPair);
之所以会产生编译错误,是因为Pair<Employee>和Pair<Manager>实际上是两种类型。
由上图可以看出,类型Pair<Manager>是类型Pair<? extends Employee>的子类型,所以为了解决这个问题可以把函数定义改成 public static void printEmployeeBoddies(Pair<? extends Employee> pair)
但是使用通配符会不会导致通过Pair<? extends Employee>的引用破坏Pair<Manager>对象呢?例如: Pair<? extends Employee> employeePair = managePair;employeePair.setFirst(new Employee("Tony", 100)); 不用担心,编译器会产生一个编译错误。Pair<? extends Employee>参数替换后,我们得到如下代码 ? extends Employee getFirst() void setFirst(? extends Employee) 对于get方法,没问题,因为编译器知道可以把返回对象转换为一个Employee类型。但是对于set方法,编译器无法知道具体的类型,所以会拒绝这个调用。
(2)超类型限定
超类型限定和子类型限定相反,可以给方法提供参数,但是不能使用返回值。? super Manager这个类型限定为Manager的所有超类。
Pair<? super Manager>参数替换后,得到如下方法
? super Manager getFirst() void setFirst(? super Manager)
编译器可以用Manager的超类型,例如Employee,Object来调用setFirst方法,但是无法调用getFirst,因为不能把Manager的超类引用转换为Manager引用。
超类型限定的存在是为了解决下面一类的问题。例如要写一个函数从Manager[]中找出工资最高和最低的两个,放在一个Pair中返回。
- public static void minMaxSal(Manager[] mgrs, Pair<? super Manager> pair) {
- if (mgrs == null || mgrs.length == 0) {
- return;
- }
- pair.setFirst(mgrs[0]);
- pair.setSecond(mgrs[0]);
- //TODO
- }
public static void minMaxSal(Manager[] mgrs, Pair<? super Manager> pair) { if (mgrs == null || mgrs.length == 0) { return; } pair.setFirst(mgrs[0]); pair.setSecond(mgrs[0]); //TODO }
如此就可以这样调用
- Pair<? super Manager> pair = new Pair<Employee>(null, null);
- minMaxSal(new Manager[] {mgr1, mgr2}, pair);
Pair<? super Manager> pair = new Pair<Employee>(null, null); minMaxSal(new Manager[] {mgr1, mgr2}, pair);
(3) <T extends Comparable<? super T>>
超类型限定还有一个使用形式: <T extends Comparable<? super T>>
比如要写一个函数返回数组中的最小对象。似乎可以如下定义。 public static <T> T min(T[] a) 但是要找到最小值必须进行比较,这样就需要T实现了Comparable接口。为此可以把方法定义改变成 public static <T extends Comparable> T min(T[] a) 因为Comparable接口本身也是泛型的,所以最终可以改写成 public static <T extends Comparable<T>> T min(T[] a)
但是考虑到如下的使用场景
- GregorianCalendar[] birthdays = {
- new GregorianCalendar(1906, Calendar.DECEMBER, 9),
- new GregorianCalendar(1815, Calendar.DECEMBER, 10),
- new GregorianCalendar(1903, Calendar.DECEMBER, 3),
- new GregorianCalendar(1910, Calendar.JUNE, 22),
- };
- System.out.println("Min Age = " + ArrayAlg.min(birthdays).getTime());
GregorianCalendar[] birthdays = { new GregorianCalendar(1906, Calendar.DECEMBER, 9), new GregorianCalendar(1815, Calendar.DECEMBER, 10), new GregorianCalendar(1903, Calendar.DECEMBER, 3), new GregorianCalendar(1910, Calendar.JUNE, 22), }; System.out.println("Min Age = " + ArrayAlg.min(birthdays).getTime());
java.util.GregorianCalendar继承自java.util.Calendar。GregorianCalendar本身没有实现Comparable接口,其父类Calendar实现了Comparable接口。 但是编译器会对ArrayAlg.min(birthdays)报错,因为GregorianCalendar实现了Comparable<Calendar> 而不是Comparable<GregorianCalendar>。 只要把min方法的申明改成<T extends Comparable<? super T>>就可以通过编译。 完整的方法如下。 注意,由于一个类可能没有实现Comparable方法,所以还需要定义另外一个方法,传入Comparator对象进行比较。
- import java.util.Comparator;
- public class ArrayAlg {
- public static <T extends Comparable<? super T>> T min(T[] a) {
- if (a == null || a.length == 0) {
- return null;
- }
- T smallest = a[0];
- for (int i = 1; i < a.length; i++) {
- if (smallest.compareTo(a[i]) > 0) {
- smallest = a[i];
- }
- }
- return smallest;
- }
- public static <T> T min(T[] a, Comparator<? super T> c) {
- if (a == null || a.length == 0) {
- return null;
- }
- T smallest = a[0];
- for (int i = 1; i < a.length; i++) {
- if (c.compare(smallest, a[i]) > 0) {
- smallest = a[i];
- }
- }
- return smallest;
- }
- }
import java.util.Comparator; public class ArrayAlg { public static <T extends Comparable<? super T>> T min(T[] a) { if (a == null || a.length == 0) { return null; } T smallest = a[0]; for (int i = 1; i < a.length; i++) { if (smallest.compareTo(a[i]) > 0) { smallest = a[i]; } } return smallest; } public static <T> T min(T[] a, Comparator<? super T> c) { if (a == null || a.length == 0) { return null; } T smallest = a[0]; for (int i = 1; i < a.length; i++) { if (c.compare(smallest, a[i]) > 0) { smallest = a[i]; } } return smallest; } }
传入Comparator作为参数的min方法的调用例子。
- Manager[] managers = new Manager[] {
- new Manager("Jack", 10000.99),
- new Manager("Tom", 10001.01),
- };
- Manager minSalMgr = ArrayAlg.min(managers, new Comparator<Employee>() {
- @Override
- public int compare(Employee e1, Employee e2) {
- return Double.compare(e1.getSalary(), e2.getSalary());
- }
- });
- System.out.println("Min. Salary = " + minSalMgr.getName());
Manager[] managers = new Manager[] { new Manager("Jack", 10000.99), new Manager("Tom", 10001.01), }; Manager minSalMgr = ArrayAlg.min(managers, new Comparator<Employee>() { @Override public int compare(Employee e1, Employee e2) { return Double.compare(e1.getSalary(), e2.getSalary()); } }); System.out.println("Min. Salary = " + minSalMgr.getName());
在Java的类库中,大量使用了这种类型的参数。例如在java.util.Collections类中就定义了下面两个方法
- public static <T extends Comparable<? super T>> void sort(List<T> list)
- public static <T> void sort(List<T> list, Comparator<? super T> c)
public static <T extends Comparable<? super T>> void sort(List<T> list) public static <T> void sort(List<T> list, Comparator<? super T> c)
(4)无限定通配符?
无限定通配符表示不需要限定任何类型。例如Pair<?>。 参数替换后的Pair类有如下方法 ? getFirst() void setFirst(?) 所以可以调用getFirst方法,因为编译器可以把返回值转换为Object。 但是不能调用setFirst方法,因为编译器无法确定参数类型,这就是Pair<?>和Pair方法的根本不同。 无限定通配符的出现是为了支持如下的函数定义。
- public class PairAlg {
- public static boolean hasNulls(Pair<?> p) {
- return p.getFirst() == null || p.getSecond() == null;
- }
- }