Java基础加强:泛型
1. 什么是泛型?
泛型(Generic type )是对 Java 语言的类型系统的一种扩展,以支持创建可以按类型进行参数化的类。
例如,Map 类允许您向一个 Map 添加任意类的对象,即使最常见的情况是在给定映射(map)中保存某个特定类型(比如 String)的对象。
因为 Map.get() 被定义为返回 Object,所以一般必须将 Map.get() 的结果强制类型转换为期望的类型,如下面的代码所示:
Map m = newHashMap();
m.put("key","blarg");
String s =(String) m.get("key");
要让程序通过编译,必须将 get() 的结果强制类型转换为 String,并且希望结果真的是一个 String。但是有可能某人已经在该映射中保存了不是 String 的东西,这样的话,上面的代码将会抛出异常,为防止出现这类情况,可以在m创建时限制它的元素类型。(泛型只在编译过程中,生成class文件后自动擦除了)
2. 泛型的好处
该代码不使用泛型:
List li = newArrayList();
li.put(newInteger(3));
Integer i =(Integer) li.get(0);
该代码使用泛型:
List<Integer>li = new ArrayList<Integer>();
li.put(newInteger(3));
Integer i =li.get(0);
3. 泛型用法的例子
泛型的许多最佳例子都来自集合框架,因为泛型让您在保存在集合中的元素上指定类型约束。考虑这个使用 Map 类的例子,其中涉及一定程度的优化,即 Map.get() 返回的结果将确实是一个 String:
Map m = new HashMap();
m.put("key", "blarg");
String s = (String) m.get("key");
如果有人已经在映射中放置了不是 String 的其他东西,上面的代码将会抛出 ClassCastException。泛型允许您表达这样的类型约束,即 m 是一个将 String 键映射到 String 值的 Map。这可以消除代码中的强制类型转换,同时获得一个附加的类型检查层,这个检查层可以防止有人将错误类型的键或值保存在集合中。
下面的代码示例展示了 JDK 5.0 中集合框架中的 Map 接口的定义的一部分:
public interface Map<K, V> {
public void put(K key, V value);
public V get(K key);
}
注意该接口的两个附加物:
类型参数 K 和 V 在类级别的规格说明,表示在声明一个 Map 类型的变量时指定的类型的占位符。
在 get()、put() 和其他方法的方法签名中使用的 K 和 V。
为了赢得使用泛型的好处,必须在定义或实例化 Map 类型的变量时为 K 和 V 提供具体的值。以一种相对直观的方式做这件事:
Map<String, String> m = newHashMap<String, String>();
m.put("key", "blarg");
String s = m.get("key");
当使用 Map 的泛型化版本时,您不再需要将 Map.get() 的结果强制类型转换为 String,因为编译器知道 get() 将返回一个 String。
在使用泛型的版本中并没有减少键盘录入;实际上,比使用强制类型转换的版本需要做更多键入。使用泛型只是带来了附加的类型安全。因为编译器知道关于您将放进 Map 中的键和值的类型的更多信息,所以类型检查从执行时挪到了编译时,这会提高可靠性并加快开发速度。
4类型通配符的作用
类型通配符中引入了类型通配符,这让您可以声明 List<?> 类型的变量。可以从list中检索元素,但是不能添加元素。
List<Integer> li = newArrayList<Integer>();
li.add(new Integer(42));
List<?> lu = li;
System.out.println(lu.get(0));
5 泛型方法
(在类的定义中添加一个形式类型参数列表,可以将类泛型化。方法也可以被泛型化,不管它们定义在其中的类是不是泛型化的。
声明泛型方法,一般是因为您想要在该方法的多个参数之间宣称一个类型约束。例如,下面代码中的 ifThenElse() 方法,根据它的第一个参数的布尔值,它将返回第二个或第三个参数:
public <T> T ifThenElse(boolean b, Tfirst, T second) {
return b ? first : second;
}
注意,您可以调用 ifThenElse(),而不用显式地告诉编译器,您想要 T 的什么值。编译器不必显式地被告知 T 将具有什么值;它只知道这些值都必须相同。编译器允许您调用下面的代码,因为编译器可以使用类型推理来推断出,替代 T 的 String 满足所有的类型约束:
String s = ifThenElse(b, "a","b").