三种通配符
Java有三种通配符限定:子类型限定通配符、超类型限定通配符、无类型通配符。
在泛型程序设计语法中,考察了下面的Pair泛型类,也提到了泛型的继承规则。无论S与T是什么关系,Pair<S>和Pair<T>没有任何关系。但当我们有这方面的需求时,就可以使用通配符类型。
public class Pair<T>{
private T first;
private T second;
public T getFirst(){ return first; }
public T getSecond(){ return second; }
public void setFirst(T newVal){ first = newVal; }
public void setSecond(T newVal){ second = newVal; }
}
通配符类型中,允许类型参数变化。
1、子类型限定通配符
Pair<? extends Employee>
表示任何Pair泛型类型,它的参数是Employee的子类。比如如果Manager是Employee的子类,那么Pair<Manager>是Pair<? extends Employee>的子类型。
注意:子类型限定通配符可以从泛型类读取,但不能向泛型类写入。用Pair泛型类举例,上面的setFirst()方法和getFirst()方法应该是下面这样:
? extends Employee getFirst() //子类型限定下的getFirst方法
void setFirst(? extends Employee) //子类型限定下的setFirst方法
因为上溯造型,无论是什么子类型,都可以上溯造型为Employee, 所以我们可以调用getFirst方法进行读取;但setFirst方法不行,编译器只知道需要Employee的一个子类型,但不知道具体类型。它拒绝传递任何特定的类型,毕竟?不能用来匹配。
2、超类型限定通配符
Pair<? super Manager>
这个通配符限制为Manager的所有超类型。带有超类型的通配符可以向泛型对象写入,但不能从泛型对象读取。
void setFirst(? super Manager)
? super Manager getFirst()
上面不是真正的Java语法,但可以了解编译器知道什么。我们可以调用setManager方法,并传入Manager对象或者它的子类型对象,但不能传入Manager的超类;另外,调用getFirst方法不能保证返回对象的类型。只能把它赋给一个Object。
直观的讲,带有超类型限定的通配符可以向泛型对象写入,带有子类型限定的通配符可以从泛型对象读取。
3、无限定通配符
Pair<?>
类型Pair<?>的两个方法可以看作:
? getFirst()
void setFirst(?)
getFirst()的返回值只能赋给一个Object。setFirst()方法不能被调用,甚至不能被Object调用。不过可以调用setFirst(null)。
Pair<?>和Pair本质区别:可以用任意Object对象调用原始类型的setObject方法(涉及到类型擦除问题)。
通配符捕获
例如我们要写一个交换方法,交换的时候必须临时保存第一个元素,但通配符不是类型变量,因此不能像下面这样写:
public static void swap(Pair<?> p){
? t = p.getFirst(); //Error
p.setFirst(p.getSecond);
p.setSecond(t);
}
上面这种方法不可取,但我们可以通过引入一个辅助方法来实现:
public static <T> void swapHelper(Pair<T> p){
T t = p.getFirst();
p.setFirst(p.getSecond);
p.setSecond(t);
}
注意,swapHelper是一个泛型方法,但swap不是,swap具有固定的Pair<?>类型的参数。现在可以通过swap调用swapHelper:
public static void swap(Pair<?> p){ swapHelper(P); }