分类: Java
2012-02-29 09:24:48
假设您具有该方法:
void printList(List l) { for (Object o : l) System.out.println(o); } |
上面的代码在 JDK 5.0 上编译通过,但是如果试图用 List
如果试图编写像下面这样的方法,那么将会怎么样?
void printList(List |
它仍然不会通过编译,因为一个 List
解决方案是使用类型通配符:
void printList(List> l) { for (Object o : l) System.out.println(o); } |
上面代码中的问号是一个类型通配符。它读作“问号”。List> 是任何泛型 List 的父类型,所以您完全可以将List
4. 类型通配符,这让您可以声明 List> 类型的变量。您可以对这样的 List 做什么呢?非常方便,可以从中检索元素,但是不能添加元素。原因不是编译器知道哪些方法修改列表哪些方法不修改列表,而是(大多数)变化的方法比不变化的方法需要更多的类型信息。下面的代码则工作得很好:List li.add(new Integer(42)); List> lu = li; System.out.println(lu.get(0)); |
为什么该代码能工作呢?对于 lu,编译器一点都不知道 List 的类型参数的值。但是编译器比较聪明,它可以做一些类型推理。在本例中,它推断未知的类型参数必须扩展 Object 。(这个特定的推理没有太大的跳跃,但是编译器可以作出一些非常令人佩服的类型推理,后面就会看到(在 底层细节 一节中)。所以它让您调用 List.get() 并推断返回类型为 Object。
另一方面,下面的代码不能工作:
List li.add(new Integer(42)); List> lu = li; lu.add(new Integer(43)); // error |
在本例中,对于 lu,编译器不能对 List 的类型参数作出足够严密的推理,以确定将 Integer 传递给 List.add() 是类型安全的。所以编译器将不允许您这么做。
以免您仍然认为编译器知道哪些方法更改列表的内容哪些不更改列表内容,请注意下面的代码将能工作,因为它不依赖于编译器必须知道关于 lu 的类型参数的任何信息:
List li.add(new Integer(42)); List> lu = li; lu.clear(); |
声明泛型方法,一般是因为您想要在该方法的多个参数之间宣称一个类型约束。例如,下面代码中的 ifThenElse()方法,根据它的第一个参数的布尔值,它将返回第二个或第三个参数:
public return b ? first : second; } |
注意,您可以调用 ifThenElse(),而不用显式地告诉编译器,您想要 T 的什么值。编译器不必显式地被告知 T 将具有什么值;它只知道这些值都必须相同。编译器允许您调用下面的代码,因为编译器可以使用类型推理来推断出,替代 T 的 String 满足所有的类型约束:
String s = ifThenElse(b, "a", "b"); |
类似地,您可以调用:
Integer i = ifThenElse(b, new Integer(1), new Integer(2)); |
但是,编译器不允许下面的代码,因为没有类型会满足所需的类型约束:
String s = ifThenElse(b, "pi", new Float(3.14));
|