你们在平时的工做学习中, 确定会见过很多以下的语句:java
List<? super T> List<? extends T>
咱们都知道, 上面的代码时关于 Java 泛型的, 那么这两个不一样的写法都有什么区别呢?学习
首先, 说到 Java 的泛型, 咱们必需要提到的是Java 泛型的类型擦除机制: Java中的泛型基本上都是在编译器这个层次来实现的. 在生成的 Java 字节代码中是不包含泛型中的类型信息的. 使用泛型的时候加上的类型参数, 会被编译器在编译的时候去掉. 这个过程就称为类型擦除. 如在代码中定义的List<Object>和List<String>等类型, 在编译以后都会变成List, JVM看到的只是List, 而由泛型附加的类型信息对JVM来讲是不可见的.code
在使用泛型类时, 咱们可使用一个具体的类型, 例如能够定义一个 List<Integer> 的对象, 咱们的泛型参数就是 Integer; 咱们也可使用通配符 ?
来表示一个未知类型, 例如 List<?> 就表示了泛型参数是某个类型, 只不过咱们并不知道它的具体类型时什么.
List<?>所声明的就是全部类型都是能够的, 但须要注意的是, List<?>并不等同于List<Object>. 对于 List<Object> 来讲, 它实际上肯定了 List 中包含的是 Object 及其子类, 咱们可使用 Object 类型来接收它的元素. 相对地, List<?> 则表示其中所包含的元素类型是不肯定, 其中可能包含的是 String, 也多是 Integer. 若是它包含了 String 的话, 往里面添加 Integer 类型的元素就是错误的. 做为对比, 咱们能够给一个 List<Object> 添加 String 元素, 也能够添加 Integer 类型的元素, 由于它们都是 Object 的子类.
正由于类型未知, 咱们就不能经过 new ArrayList<?>()
的方法来建立一个新的ArrayList 对象, 由于编译器没法知道具体的类型是什么. 可是对于 List<?> 中的元素, 咱们却均可以使用 Object 来接收, 由于虽然类型未知, 但确定是Object及其子类.对象
咱们在上面提到了, List<?> 中的元素只能使用 Object 来引用, 这样做确定时不太方便的, 不过幸运的是, Java 的泛型机制容许咱们对泛型参数的类型的上界和下界作一些限制, 例如 List<? extends Number> 定义了泛型的上界
是 Number, 即 List 中包含的元素类型是 Number 及其子类. 而 List<? super Number> 定义了泛型的下界
, 即 List 中包含的是 Number 及其父类.
当引入了泛型参数的上界和下界后, 咱们编写代码相对来讲就方便了许多, 不过也引入了新的问题, 即咱们在何时使用上界, 何时使用下界, 以及它们的区别和限制到底时什么? 下面我来讲说个人理解.get
? extends T 描述了通配符上界, 即具体的泛型参数须要知足条件: 泛型参数必须是 T 类型或它的子类
, 例如:编译器
List<? extends Number> numberArray = new ArrayList<Number>(); // Number 是 Number 类型的 List<? extends Number> numberArray = new ArrayList<Integer>(); // Integer 是 Number 的子类 List<? extends Number> numberArray = new ArrayList<Double>(); // Double 是 Number 的子类
上面三个操做都是合法的, 由于 ? extends Number 规定了泛型通配符的上界, 即咱们实际上的泛型必需要是 Number 类型或者是它的子类, 而 Number, Integer, Double 显然都是 Number 的子类(类型相同的也能够, 即这里咱们能够认为 Number 是 Number 的子类).io
假设有类型 G, 以及 SuperClass 和 SubClass 两个类, 而且 SuperClass 是 SubClass 的父类, 那么:编译
能够想象 G<? extends T> 为一个左闭右开的区间(T 在最左边), G<? extends Object> 是最大的区间, 当区间 G<? extends SuperClass> 包含 区间 G<? extends SubClass>时, 那么较大的区间就是父类.
class
根据上面的例子, 对于 List<? extends Number> numberArray 对象:泛型
根据上面的例子, 对于 List<? extends Number> numberArray 对象:
即, 咱们不能添加任何对象到 List<? extends T> 中, 由于咱们不能肯定一个 List<? extends T> 对象实际的类型是什么, 所以就不能肯定插入的元素的类型是否和这个 List 匹配. List<? extends T> 惟一能保证的是咱们从这个 list 中读取的元素必定是一个 T 类型的.
? super T 描述了通配符下界, 即具体的泛型参数须要知足条件: 泛型参数必须是 T 类型或它的父类
, 例如:
// 在这里, Integer 能够认为是 Integer 的 "父类" List<? super Integer> array = new ArrayList<Integer>(); // Number 是 Integer 的 父类 List<? super Integer> array = new ArrayList<Number>(); // Object 是 Integer 的 父类 List<? super Integer> array = new ArrayList<Object>();
对于上面的例子中的 List<? super Integer> array 对象:
对于上面的例子中的 List<? super Integer> array 对象:
有一点须要注意的是, List<? super T>
和 List<? extends T>
中, 咱们所说的 XX 是 T 的父类(a superclass of T)
或 XX 是 T 的子类(a subclass of T)
实际上是针对于泛型参数而言的. 例如考虑以下例子:
List<? super Integer> l1 = ... List<? extends Integer> l2 = ...
那么这里 ? super Integer
和 ? extends Integer
的限制是对谁的呢? 是表示咱们能够插入任意的对象 X 到 l1 中, 只要 X 是 Integer 的父类? 是表示咱们能够插入任意的对象 Y 到 l2 中, 只要 Y 是 Integer 的子类?
其实不是的, 咱们必需要抛弃上面的概念, ? super Integer
和 ? extends Integer
限制的实际上是 泛型参数
, 即 List<? super Integer> l1
表示 l1
的泛型参数 T 必需要知足 T 是 Integer 的父类
, 所以诸如 List<Object>
, List<Number
的对象就能够赋值到 l1 中. 正由于咱们知道了 l1 中的泛型参数的边界信息, 所以咱们就能够向 l1 中添加 Integer 对象了, 推理过程以下:
令 T 是 l1 的泛型参数, 即: l1 = List<T> = List<? super Integer> 所以有 T 是 Integer 或 Integer 的父类. 若是 T 是 Integer, 则 l1 = List<Integer>, 显然咱们能够添加任意的 Integer 对象或 Integer 的子类对象到 l1 中. 若是 T 是 Integer 的父类, 那么同理, 对于 Integer 或 Integer 的子类的对象, 咱们也能够添加到 l1 中.
按一样的分析方式, List<? extends Integer> l2
表示的是 l2 的泛型参数是 Integer 的子类型. 而若是咱们要给一个 List<T> 插入一个元素的话, 咱们须要保证此元素是 T 或是 T 的子类, 而这里 List<? extends Integer> l2
, l2 的泛型参数是什么类型咱们都不知道, 进而就不能肯定 l2 的泛型参数的子类是哪些, 所以咱们就不能向 l2 中添加任何的元素了.
来一个对比:
List<? super Integer> l1
:? super Integer
限定的是泛型参数. 令 l1 的泛型参数是 T, 则 T 是 Integer 或 Integer 的子类, 所以 Integer 或 Integer 的子类的对象就能够添加到 l1 中.List<? extends Integer> l2
:? extends Integer
限定的是泛型参数. 令 l2 的泛型参数是 T, 则 T 是 Integer 或 Integer 的子类, 进而咱们就不能找到一个类 X, 使得 X 是泛型参数 T 的子类, 所以咱们就不能够向 l2 中添加元素. 不过因为咱们知道了泛型参数 T 是 Integer 或 Integer 的子类这一点, 所以咱们就能够从 l2 中读取到元素, 并能够存放到 Integer 中.PECE 原则: Producer Extends, Consumer Super
例子:
public class Collections { public static <T> void copy(List<? super T> dest, List<? extends T> src) { for (int i=0; i<src.size(); i++) dest.set(i,src.get(i)); } }
上面的例子是一个拷贝数据的代码, src 是 List<? extends T> 类型的, 所以它能够读取出 T 类型(或其子类)的数据, dest 是 List<? super T> 类型的, 所以它能够写入 T 类型或其父类的数据.
Java深度历险(五)——Java泛型
difference-between-super-t-and-extends-t-in-java