9.3.3 设定类型通配符的下限
9.3.3 设定类型通配符的下限
除可以指定通配符的上限之外,Java
也允许指定通配符的下限,通配符的下限用<? super 类型>
的方式来指定,通配符下限的作用与通配符上限的作用恰好相反。
逆变
指定通配符的下限就是为了支持类型型变。比如Son
是Father
的子类,当程序需要一个A<? super Father>
变量时,程序可以将A<Father>
、A<Object>
赋值给A<? super Father>
类型的变量,这种型变方式被称为逆变。
对于逆变的泛型集合来说,编译器只知道集合元素是下限的父类型,但具体是哪种父类型则不确定。因此,这种逆变的泛型集合能向其中添加元素(因为实际赋值的集合元素总是逆变声明的父类),从集合中取元素时只能被当成Object
类型处理(编译器无法确定取出的到底是哪个父类的对象)。
假设自己实现一个工具方法:实现将src
集合中的元素复制到dest
集合的功能,因为dest
集合可以保存src
集合中的所有元素,所以dest
集合元素的类型应该是sre
集合元素类型的父类。
对于上面的copy
方法,可以这样理解两个集合参数之间的依赖关系:不管src
集合元素的类型是什么,只要dest
集合元素的类型与前者相同
或者是前者的父类
即可,此时通配符的下限
就有了用武之地。下面程序采用通配符下限的方式来实现该copy
方法。
1 | import java.util.*; |
使用这种语句,就可以保证程序的代码1 处调用后推断出最后一个被复制的元素类型是Integer
,而不是笼统的Number
类型。
上面方法用到了泛型方法的语法,就是在方法修饰符和返回值类型之间用<>
定义泛型形参。关于泛型方法更详细介绍可参考下一节。
实际上,Java
集合框架中的TreeSet<E>
有一个构造器也用到了这种设定通配符下限的语法,如下所示:
1 | package java.util; |
正如前一章所介绍的, TreeSet
会对集合中的元素按自然顺序
或定制顺序
进行排序。如果需要TreeSet
对集合中的所有元素进行定制排序,则要求TreeSet
对象有一个与之关联的Comparator
对象。上面构造器中的参数comparator
就是进行定制排序的Comparator
对象。
Comparator
接口也是一个带泛型声明的接口
1 | ... |
通过这种带下限的通配符的语法,可以在创建TreeSet
对象时灵活地选择合适的Comparator
。假定需要创建一个TreeSet<String>
集合,并传入一个可以比较String
大小的Comparator
,这个Comparator
既可以是Comparator<String>
,也可以是Comparator<Object>
——只要尖括号里传入的类型是String
的父类型(或它本身)即可。
1 | import java.util.*; |
通过使用这种通配符下限的方式来定义TreeSet
构造器的参数,就可以将所有可用的Comparator
作为参数传入,从而增加了程序的灵活性。当然,不仅TreeSet
有这种用法, TreeMap
也有类似的用法,具体的请查阅Java
的API
文档。