在学习 Spring 的依赖注入时, 被 Google 导流到了 [Java Generics FAQs](). 这篇文章深刻讲解了 Java 中泛型相关的方方面面, 阅读完毕后, 整理了本身的一些理解.html
在进入具体的讨论以前, 咱们须要先明确几个名词的含义.java
interface List<E> {}
为例, List<E>
是 generic type, E
是 type parameter.List<String> stringList;
为例, List<String>
是 parameterized type, 是 List<E>
的一个实例, String
是 type argument.泛型中将通配符(wildcard)分为三类:数组
后二者也被统称为 bounded wildcard.
结合通配符, parameterized type 也能够划分为三类.oracle
conceret type argument generic type ---------------------------> conceret parameterized type unbound type argument generic type ---------------------------> unbound parameterized type bounded type argument generic type ---------------------------> bounded parameterized type
Raw type 的存在是为了兼容引入泛型以前的版本, 大多数时候你均可以不用考虑它.学习
严格说来, 泛型只存在于编译期间, JVM 并不感知泛型. 在编译时, 编译器经过 type erasure 来消除 type parameter 和 type argument.
具体的处理方式是:spa
何为上确界, 对于 upper bounded type paramter 而言, 是指其公共父类, <? extends Number> 对应的就是 Number.
对于其余类型的 type paramter 而言, 由于 type argument 只能是引用类型(reference type), 而引用类型的公共父类是 Object, 因此其上确界都是 Object.code
咱们能够从 Java Generics FAQs 的例子中看到具体的转换过程. 左边是原始的代码, 右边是通过 type erasure 转换后的结果.orm
这个例子同时也反应了, 在 type erasure 的过程当中, 编译器可能会按需加入 bridge method 和 type cast.htm
泛型的引入使得对象之间的继承关系变得更复杂, 以下这个例子中的一部分就是错误的.对象
public class SuperDemo { public static void main(String args[]) { List<Number> a = new ArrayList<Number>(); ArrayList<Number> b = new ArrayList<Integer>(); List<? extends Number> c = new ArrayList<Integer>(); List<? super Number> d = new ArrayList<Object>(); List<? super Integer> e = d; } }
理论上, 泛型相关的继承关系判断须要从两个纬度考虑:
具体而言. 对于 type argument 相同的状况, generic type 之间的继承关系决定两个 parameterized type 的父子关系, 因此 List<Number>
是 ArrayList<Number>
的父类.
但 ArrayList<Number>
不是 ArrayList<Integer>
的父类, type argument 不一样的状况下, 泛型之间的继承关系判断会很复杂. 主要是因为 wildcard 的存在, 致使 type argument 能够表明一类类型, 因此要引入集合中的超集(superset)概念, 即一方所表明的全部类型彻底包含在以一方内.
最终的判断标准是, 在 type argument 不相同的状况下, 若是 type argument 是对方的超集, 并且 generic type 与对方相同或者是对方的父类, 那么当前的 parameterized type 才是对方的父类.
这时候再来回答如下的问题就会比较简单了:
观察泛型在异常处理和数组中的使用限制, 思考是什么致使了这些限制, 在必定程度上能够验证本身以前的理解是否正确.
Java 的异常处理在运行时生效, 而 type erasure 发生在编译期间, 因此大多数时候, 泛型在异常处理中并无用武之地.
Throwable
的子类是泛型.与异常处理相似, 数组在运行时保存了每一个元素的类型信息, 因此泛型数组也是一个没有太大意义的概念. 虽然能够定义一个数据的元素为泛型, 但咱们仅能新建元素为 unbound parameterized type 的泛型数组. 具体而言, 下例子中 line 1 和 line 2 合法, 但 line 3 是错误的.
List<String>[] a; List<?>[] b = new List<?>[10]; a = new List<String>[10]; // error
究其根本, 是由于数据的组成元素都应该是同一类型的: An array is a container object that holds a fixed number of values of a single type. 而同一 generic type 对应的不一样实例实质上并不等价, 但通过 type erasure 后, 并不能在运行时区分出这些.
假如能新建元素为 concerete parameterized type 的数组, 考虑以下案例.
List<String>[] stringLists = new List<String>[10]; stringLists[0] = new List<String>(); stringLists[1] = new List<Integer>();