【每日新知】
泛型是一种表示类或方法行为对于未知类型的类型约束的方法,比如 “不管这个方法的参数x 和 y 是哪种类型,它们必须是相同的类型”,“必须为这些方法提供同一类型的参数” 或者 “foo() 的返回值和 bar() 的参数是同一类型的”。通配符 — 使用一个奇怪的问号表示类型参数 — 是一种表示未知类型的类型约束的方法。通配符并不包含在最初的泛型设计中(起源于 Generic Java(GJ)项目),从形成 JSR 14 到发布其最终版本之间的五年多时间内完成设计过程并被添加到了泛型中。通配符在类型系统中具有重要的意义,它们为一个泛型类所指定的类型集合提供了一个有用的类型范围。
对泛型类 ArrayList 而言,对于任意(引用)类型 T,ArrayList<?> 类型是ArrayList<T> 的超类型(类似原始类型 ArrayList 和根类型 Object,但是这些超类型在执行类型推断方面不是很有用)。通配符类型 List<?> 与原始类型 List 和具体类型 List<Object> 都不相同。如果说变量 x具有 List<?> 类型,这表示存在一些 T 类型,其中 x 是 List<T>类型,x 具有相同的结构,尽管我们不知道其元素的具体类型。这并不表示它可以具有任意内容,而是指我们并不了解内容的类型限制是什么 — 但我们知道存在 某种限制。另一方面,原始类型 List 是异构的,我们不能对其元素有任何类型限制,具体类型 List<Object> 表示我们明确地知道它能包含任何对象(当然,泛型的类型系统没有 “列表内容” 的概念,但可以从 List 之类的集合类型轻松地理解泛型)。通配符在类型系统中的作用部分来自其不会发生协变(covariant)这一特性。数组是协变的,因为 Integer 是 Number 的子类型,数组类型 Integer[] 是 Number[] 的子类型,因此在任何需要 Number[] 值的地方都可以提供一个 Integer[] 值。另一方面,泛型不是协变的,List<Integer> 不是 List<Number> 的子类型,试图在要求 List<Number> 的位置提供List<Integer> 是一个类型错误。这不算很严重的问题 — 也不是所有人都认为的错误 — 但泛型和数组的不同行为的确引起了许多混乱。