范型在Java开发中使用很广泛,主要是用于参数化类型,可以让我们在定义变量或者方法时,不用限制类型,让代码更加灵活、可扩展。范型有两个约束,一个是上限约束<T extends X>
,一个下限约束<T super X>
,平时开发中用到了,但是可能没有太多关注,下面就分析一下范型的上下限约束。
指的是在传递类型参数时,只能传递指定类型或者是继承或实现指定类型的对象,使用extends关键字,比如<T extends Number>、<? extends Number>。范型上限约束常用在类的声明,方法的声明上,如下:
class Calculator<T extends Number> {
T value;
}
<T extends Number> void add(List<T> list) {}
void replace(List<? extends Number> list) {}
思考一下,下面几个范型上限约束的使用示例中有什么问题以及产生问题的原因。
1class Calculator<T extends Number> {
2 T value;
3}
4
5//1
6Calculator<Integer> calculator1 = new Calculator<>();
7//2
8Calculator<String> calculator2 = new Calculator<>();
9
10<T extends Number> void add1(List<T> list, Number value) {
11 //3
12 list.add(value)
13}
14
15<T extends Number> void add2(List<T> list, T value) {
16 //4
17 list.add(value)
18}
19
20void replace(List<? extends Number> list) {
21 //5
22 Number oldVal = list.get(0);
23 Number newVal = 1;
24 //6
25 list.add(0, newVal);
26}
指的是在传递类型参数时,只能传递指定类型或者其直接父类或间接父类以及其实现的接口,使用super关键字,比如<? super Integer>。范型下限约束不能用在类的申明上,而且只能通过通配符?
的方式使用在方法的声明上,如下:
void replace(List<? super Integer> list) { }
同样的思考一下,下面这段代码中,范型下限约束的使用有什么问题以及产生问题的原因。
void replace(List<? super Integer> list) {
//1
Integer oldVal = list.get(0);
Integer newVal = 1;
//2
list.add(0, newVal);
}
范型上限约束使用的场景比较多,而且理解起来很容易也很自然,虽然有上限约束,但是我们还是可以调用上限类型中的一些方法来做一些封装。但是下限约束使用场景的较少,下面以最常提到的List.sort(Comparator<? super E> c)方法来解释一下。首先我们考虑一下,如果sort方法定义是这样的话会怎么样。
void sort(Comparator<E> c)
虽然这样并不会影响功能,仍然可以正常使用,但是我们考虑一种场景, 我们有一个父类Parent,有两个子类Child1和Child2,要实现根据Parent中的priority字段进行排序。
1class Parent {
2 protected int priority;
3}
4
5class Child1 extends Parent { }
6class Child2 extends Parent { }
7
8Comparator<Parent> comparator = new Comparator<Parent>() {...};
9List<Child1> child1List = new ArrayList<>();
10child1List.sort(comparator);
11List<Child2> child2List = new ArrayList<>();
12child2List.sort(comparator);
如果sort方法定义是void sort(Comparator<E> c)
的话,那么这个comparator是无法给这两个列表排序用,因为Comparator的范型类型不匹配,这么一想由于范型下限的使用使得扩展性更好了。