CoreJava笔记 - 范型程序设计(4)
通配符类型
-
什么是通配符类型
Employee
是Manager
的超类。定义一个打印方法:public static void printBuddies(Pair<Employee>)
,但是无法将Pair<Manager>传参。这时应该采用通配符类型:public static void printBuddies(Pair
<? extends Employee>
)
。
这种方式不会影响get
方法,但是set
方法可能会发生编译错误。总结:限定了子类型的通配符用于从范型对象读取。
-
super bound
限定了类型必须是某个类型的基类:public static void minmax(Manager[] a,
Pair<? super Manager> result
)
。在这个例子中,传入的是Manager[]
,但是结果完全可以保存在Pair<Employee>
或者Pair<Object>
中。总结:带有超类型限定的通配符可以向范型对象写入。
扩展阅读:一个变态一点儿的例子:
声明函数:`public static <T extends Comparable> T min(T[] a)`。因为`Comparable`本身就是范型类,如果更讲究一点儿:`public static <T extends Comparable<T>> T min(T[] a)`。 在大多数情况下,这段代码都工作得非常好,直到遇到`LocalDate`类,`LocalDate`实现了`ChronoLocalDate`接口,而`ChronoLocalDate`扩展了`Comparable<ChronoLocalDate>`接口。因此`LocalDate`实现的接口是`Comparable<ChronoLocalDate>`,而不是`Comparable<LocalDate>`。 此时,函数应该声明为:`public static <T extends Comparable<? super T>> T min(T[] a)`,而接口中的方法声明为:`int compareTo(? super T)`。
注意:子类型限定的一个常用用法是作为一个函数式接口的参数类型。如Collection接口的一个方法:
default boolean removeIf(Predicate<? super E> filter)
-
无限定通配符
无限定通配符Pair<?>
在使用时会有比较严格的限定。? getFirst()
是能返回Object
类型,而setFirst(?)
根本无法调用(除了以null
作为参数)。
使用无限定通配符的场景:// 在这个方法中,根本不涉及到类型T,因此用无限定通配符的可读性好一些 public static boolean hasNulls(Pair<?> p) { return p.getFirst() == null || p.getSecond() == null; } // 范型版本的声明如下: public static <T> boolean hasNulls(Pair<T> p) {...}
-
通配符的捕获
通配符广泛应用在范型库的开发中,但是通配符?
不是一个合法的类型。如果需要用到类型T
来捕获类型的值,就需要用到普通范型方法。
看下面的例子:// 函数库中有交换函数,用逻辑上,交换过程不关心数据类型,但在交换过程中要暂时性的缓存一个数据。 // 缓存的数据必须要保存在一个有明确类型的变量中。 public static void swap(Pair<?> p) {swapHelper(p);} // 用普通范型方法swapHelper() public static <T> void swapHelper(Pair<T> p) { T t = p.getFirst(); p.setFirst(p.getSecond()); p.setSecond(t); }
上面的例子有个问题:它其实可以使用普通范型,根本不需要通配符。但是下面的例子则只能使用通配符了:
// 不是minmax,而是maxmin。需要一次swap public static void maxminBonus(Manager[] a, Pair<? super Manager> result) { minmax(a, result); swap(result); }