通配符概念通配符类型中,允许类型参数变化。例如,通配符类型,子类型限定:
pair <? extends Employee > 表示任何泛型 Pair 类型,它的类型参数是 Employee 或 Employee 的子类,如 Pair,但不是 Pair。 假设要编写一个打印雇员对的方法,像这样:
public static void printBuddies ( Pair < Employee > p ) { Employee first = p . getFirst (); Employee second = p . getSecond (); System . out . println ( first . getName () + " and " + second . getName () + " are buddies." ); } 不能将 Pair 传递给这个方法,这一点很受限制。解决的方法很简单:使用通配符类型:
public static void printBuddies ( Pair <? extends Employee > p ) { 这样 Pair 是 Pair<?extends Employee> 的子类型 这样我们就可以在方法中使用 ? extends :
public static add ( Pair <? extends Number > p ) { Number first = p . getFirst (); Number second = p . getSecond (); p . setFirst ( new Integer ( first . intVlaue () + 100 )); // compile-time error p . setSecond ( new Integer ( second . intVlaue () + 100 )); // compile-time error return p . getFirst (). intValue () + p . getSecond (). intValue (); } 可以看到是可以使用 get 方法的,因为你的限定符是属于 Number 或 Number 的子类,可以安全的赋值给 Number。但是 set 方法却报错了,根据上述代码,我们如果传入一个 Pair,显然它是满足 <? extends Number> 的,但是,Pair 的 setFirst() 显然无法接受 Integer 类型。 对于 set 方法,编译器只知道需要某个 Number 的子类型,但又不知道具体是那个类型。编译器拒绝这样的方式,但是使用 get 方法就不存在这个问题。
set 方法中唯一的例外是可以传入 null
我们可以看到可以用 extends 通配符表示可以读,不能写。
通配符的超类型限定还有另外一个限定是超类型限定:
? super Manager 这个通配符限制为 Manager 或 Manager 的所有超类型。 带有超类型限定的通配符的子类型限定的通配符介绍的相反。可以为方法提供参数,但不能使用返回值。 可以来看一个例子:
static void setSame ( Pair <? super Integer > p , Integer n ) { p . setFirst ( n ); p . setSecond ( n ); } 因为是 <? super Integer> 因此可以安全的传入 Integer,不仅如此,p 也能传入 Pair 和 Pair 。因为 Number 和 Object 是 Integer 的父类。 但是 get 方法就不行了:
static void getF ( Pair <? super Integer > p ) { Integer x = p . getFirst () // Error } 如果传入的是 Pair ,编译器无法将 Number 类型转换为 Integer。
在超类型限定中,可以使用 Object 接受 get 方法
简单的说用 super 通配符表示只能写,不能读。 超类型限定还有另一中应用。Comparable 接口本身就是一个泛型类型。声明如下:
public interface Comparable < T > { public int compareTo ( T other ); } 现在有这样一个泛型方法,求数组中的最小数,我们规定必须,传入的类型变量必须实现了 Comparable,就可以这样声明:
public static < T extends Comparable < T >> T min ( T [] a ) 看起来,这样写比只使用 T extents Comparable 更彻底,并且对许多类来讲,工作得更好。例如,如果计算一个 String 数组的最小值,T 就是 String 类型的,而 String 是 Comparable 的子类型。但是,处理一个 LocalDate 对象的数组时,会出现一个问题。LocalDate 实现了 ChronoLocalDate,而 ChronoLocalDate 扩展了 Comparable。因此,LocalDate 实现的是 Comparable 而不是 Comparable。 所以要加上超类型:
public static < T extends Comparable <? super T >> T min ( T [] a ) 现在的 compareTo 方法就成了:
in compareTo (? super T ) 有可能被声明为使用类型T的对象,也有可能使用 T 的超类型(如当 T 是 LocalDate,T 的一个子类型)。无论如何,传递一个 T 类型的对象给 compareTo 方法都是安全的。
无限定通配符还有一种 无限定通配符 :Pair<?>。 无限定通配符既不能读,也不能写。get 方法只能赋值给 Object。set 方法只能使用 null 调用。 为什么要使用这样脆弱的类型?它对于许多简单的操作非常有用。例如,下面这个方法将用来测试一个pair是否包含一个null引用,它不需要实际的类型。
public static boolean hasNulls ( Pair <?> p ) { return p . getFist () == null || p . getSecond () == null ; } 通过将 hasNulls 转换成泛型方法,可以避免使用通配符类型:
public static < T > boolean hasNull ( Pair < T > p ) 但是,带有通配符的版本可读性更强。
通配符捕获编写一个交换成对元素的方法:
public static void swap ( Pair <?> p ) 通配符不是类型变量,因此,不能在编写代码中使用 ? 作为一种类型。也就是说,下述代码是非法的:
? t = p . getFirst (); // Error p . setFirst ( p . getSecond ()); p . setSecond ( t ); 我们可以写一个辅助泛型方法 swapHelper 来解决来捕获通配符:
public static < T > void swapHelper ( Pair < T > p ) { T t = p . getFirst (); p . setFirst ( p . getSecond ()); p . setSecond ( t ); } 任何,就可以在 swap 中调用 swapHelper:
public static void swap ( Pair <?> p ) { swapHelper ( p ); }