通配符概念通配符类型中,允许类型参数变化。例如,通配符类型,子类型限定:
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 ); }