通配符与上下界
类型通配符
泛型通配符一般是使用
public void test(List<Object> list){
for(int i=0;i<list.size();i++){
System.out.println(list.get(i));
}
}
值得注意的是,该<Object>
并不是像以前那样有继承关系的,也就是说 List<Object>
和 List<String>
是毫无关系的。
public void test(List<?> list){
for(int i=0;i<list.size();i++){
System.out.println(list.get(i));
}
}
泛型上下界
在使用泛型的时候,我们还可以为传入的泛型类型实参进行上下界的限制,如:类型实参只准传入某种类型的父类或某种类型的子类。泛型上下界的声明,必须与泛型的声明放在一起。
-
上界通配符(extends
) :上界通配符为”extends”,可以接受其指定类型或其子类作为泛参。其还有一种特殊的形式,可以指定其不仅要是指定类型的子类,而且还要实现某些接口。例如:List<? extends A> 表明这是A 某个具体子类的List ,保存的对象必须是A 或A 的子类。对于List<? extends A> 列表,不能添加A 或A 的子类对象,只能获取A 的对象。 -
下界通配符(super
) :下界通配符为”super”,可以接受其指定类型或其父类作为泛参。例如:List<? super A> 表明这是A 某个具体父类的List ,保存的对象必须是A 或A 的超类。对于List<? super A> 列表,能够添加A 或A 的子类对象,但只能获取Object 的对象。
PECS(Producer Extends Consumer Super)原则:
- 作为生产者提供数据(往外读取)时,适合用上界通配符(extends
) ; - 作为消费者消费数据(往里写入)时,适合用下界通配符(super
) 。
设定通配符上限
譬如
public static void main(String[] args) {
//List集合装载的是Integer,可以调用该方法
List<Integer> integer = new ArrayList<>();
test(integer);
//List集合装载的是String,在编译时期就报错了
List<String> strings = new ArrayList<>();
test(strings);
}
public static void test(List<? extends Number> list) {
}
/** 数字支撑类 */
@Getter
@Setter
@ToString
public class NumberHolder<T extends Number> {
/** 通用取值 */
private T value;
/** 构造函数 */
public NumberHolder() {}
/** 构造函数 */
public NumberHolder(T value) {
this.value = value;
}
}
/** 打印取值函数 */
public static <T extends Number> void printValue(GenericHolder<T> holder) {
System.out.println(holder.getValue());
}
设定通配符下限
//传递进来的只能是Type或Type的父类
<? super Type>
public TreeSet(Comparator<? super E> comparator) {
this(new TreeMap<>(comparator));
}
当我们想要创建一个 TreeSet<String>
类型的变量的时候,并传入一个可以比较Comparator<String>
,还可以是类型参数是Comparator<Objcet>
。无论是设定通配符上限还是下限,都是不能操作与对象有关的方法,只要涉及到了通配符,它的类型都是不确定的。
通配符和泛型方法
大多时候,我们都可以使用泛型方法来代替通配符的:
//使用通配符
public static void test(List<?> list) {}
//使用泛型方法
public <T> void test2(List<T> t) {}
如果参数之间的类型有依赖关系,或者返回值是与参数之间有依赖关系的。那么就使用泛型方法。如果没有依赖关系的,就使用通配符,通配符会灵活一些。