泛型上下限的使用场景

假设我们有两个泛型的 List

List<SuperClass> listA;
List<SubClass> listB;

A,B 具有继承关系,具体定义如下:

class SuperClass {
	public String superFun(){
    	return "Super";
    }
}

class SubClass extends SuperClass {
	public String subFun(){
    	return "Sub";
    }
}

考虑下面两种使用场景:

将 List<SubClass> 视为 List<SuperClass>,调用元素对象的超类方法(协变)

在这里 List<SubClass> 是 List<SuperClass> 的子类,与 SubClass 是 SuperClass 的子类保持一致,称作协变。

public void Main(String[] args){
	...
    List<SubClass> subList = ...;
    List<SuperClass> superList = subList;
    for(Super sup: superList){
    	sup.superFun();
    }
    ...
}

将 List<SuperClass> 视为 List<SubClass>,加入 source 中的所有元素(逆变)

在这里 List<SuperClass> 是 List<SubClass> 的子类,与 SuperClass 是 SubClass 的父类相反,称作逆变。

public void copy(List<SuperClass> dest, List<SubClass> source){
	...
    dest.addAll(source);
    ...
}

Java 的泛型主要就是为了兼容上面两种的使用场景。

上面两份代码都会编译出错,原因 Java 编译器无法解决泛型的类型转换(即使具有继承关系),Java 使用泛型参数的类默认是不变的(与协变和逆变同类的术语),即两个使用不同泛型参数的 List 是没有父子类关系的。于是就需要使用 <? extends T>,<? super T>,指定协变或者逆变,来帮助编译器进行编译时的类型检查。

  • super:逆变,泛型类的父子类关系与使用的参数类型相反。
  • extends:协变,泛型类的父子类关系与使用的参数类型一致。

于是,上面两份代码变化:

public void Main(String[] args){
	...
    List<SubClass> subList = ...;
    List<? extends SuperClass> superList = subList;
    for(Super sup: superList){
    	sup.superFun();
    }
    ...
}
public void copy(List<? super SubClass> dest, List<SubClass> source){
	...
    dest.addAll(source);
    ...
}