推荐命名类型参数
K —— 键,比如映射的键。
V —— 值,比如 List 和 Set 的内容,或者 Map 中的值。
E —— 异常类。
T —— 泛型。
泛型不是协变的
List<Object> 不是 List<String> 的父类型。
对于数组,如果 A 扩展 B,那么 A 的数组也是 B 的数组,并且完全可以在需要 B[] 的地方使用 A[]:
Integer[] intArray = new Integer[10]; Number[] numberArray = intArray;
上面的代码是有效的,因为Integer是Number的子类。
但是对于泛型来说则不然。下面的代码是无效的:
List<Integer> intList = new ArrayList<Integer>(); List<Number> numberList = intList; // invalid
大多数 Java 程序员觉得这缺少协变很烦人,或者甚至是坏的,但是之所以这样有一个很好的原因。
如果可以将 List<Integer> 赋给 List<Number>,下面的代码就会违背泛型应该提供的类型安全:
List<Integer> intList = new ArrayList<Integer>(); List<Number> numberList = intList; // invalid numberList.add(new Float(3.1415));
因为 intList 和 numberList 都是有别名的,如果允许的话,上面的代码就会让您将不是 Integers 的东西放进 intList 中。
类型通配符
void printList(List<Object> l) {
for (Object o : l)
System.out.println(o);
}
编译不过,因为一个 List<Integer> 不是一个 List<Object>(泛型不是协变的)。
现在您的泛型版本还没有普通的非泛型版本有用!
解决方案是使用类型通配符:
void printList(List<?> l) {
for (Object o : l)
System.out.println(o);
}
上面代码中的问号是一个类型通配符。
List<?> 是任何泛型 List 的父类型,所以您完全可以将 List<Object>、List<Integer> 或 List<List<List<Integer>>> 传递给 printList()。
类型通配符的作用
可以从中检索元素,但是不能添加元素。
原因是变化的方法比不变化的方法需要更多的类型信息。
下面的代码则工作得很好:
List<Integer> li = new ArrayList<Integer>(); li.add(new Integer(42)); List<?> lu = li; System.out.println(lu.get(0));
为什么该代码能工作呢?对于 lu,编译器一点都不知道 List 的类型参数的值。但是编译器比较聪明,它可以做一些类型推理。
下面的代码不能工作:
List<Integer> li = new ArrayList<Integer>(); li.add(new Integer(42)); List<?> lu = li; lu.add(new Integer(43)); // error
对于 lu,编译器不能对 List 的类型参数作出足够严密的推理,以确定将 Integer 传递给 List.add() 是类型安全的。所以编译器将不允许您这么做。
请注意下面的代码将能工作,因为它不依赖于编译器必须知道关于 lu 的类型参数的任何信息:
List<Integer> li = new ArrayList<Integer>(); li.add(new Integer(42)); List<?> lu = li; lu.clear();
限制类型
在泛型方法的例子中,类型参数 V 是无约束的或无限制的 类型。有时需要对类型参数指定附加的约束。
例子 Matrix 类,它使用类型参数 V,该参数由 Number 类来限制:
public class Matrix<V extends Number> { ... }
编译器允许您创建 Matrix<Integer> 或 Matrix<Float> 类型的变量,但是如果您试图定义 Matrix<String> 类型的变量,则会出现错误。
类型参数 V 被判断为由 Number 限制 。
在没有类型限制时,类型参数由 Object 限制。
泛型方法
public class GenMethod { public static <T> void display(T t) { System.out.println(t.getClass()); } }
泛型类
public interface Map<K,V> { V put(K key, V value); }