java泛型

泛型 Generic type 或者 generics )是对  Java   语言的类型系统的一种扩展,以支持创建可以按类型进行参数化的类。可以把类型参数看作是使用参数化类型时指定的类型的一个占位符,就像方法的形式参数是运行时传递的值的占位符一样。

可以在集合框架( Collection framework )中看到 泛型 的动机。例如, Map 类允许您向一个 Map 添加任意类的对象,即使最常见的情况是在给定映射( map )中保存某个特定类型(比如 String )的对象。

因为 Map.get() 被定义为返回 Object ,所以一般必须将 Map.get() 的结果强制类型转换为期望的类型,如下面的代码所示:

Map m = new HashMap();
m.put("key", "blarg");
String s = (String) m.get("key");
 

 

要让程序通过编译,必须将 get() 的结果强制类型转换为 String ,并且希望结果真的是一个 String 。但是有可能某人已经在该映射中保存了不是 String 的东西,这样的话,上面的代码将会抛出 ClassCastException

理想情况下,您可能会得出这样一个观点,即 m 是一个 Map ,它将 String 键映射到 String 值。这可以让您消除代码中的强制类型转换,同时获得一个附加的类型检查层,该检查层可以防止有人将错误类型的键或值保存在集合中。这就是 泛型 所做的工作。

  泛型 的好处

Java   语言中引入 泛型 是一个较大的功能增强。不仅语言、类型系统和编译器有了较大的变化,以支持 泛型 ,而且类库也进行了大翻修,所以许多重要的类,比如集合框架,都已经成为 泛型 化的了。这带来了很多好处:

类型安全。   泛型 的主要目标是提高   Java   程序的类型安全。通过知道使用 泛型 定义的变量的类型限制,编译器可以在一个高得多的程度上验证类型假设。没有 泛型 ,这些假设就只存在于程序员的头脑中(或者如果幸运的话,还存在于代码注释中)。

Java   程序中的一种流行技术是定义这样的集合,即它的元素或键是公共类型的,比如 “String 列表 或者 “String String 的映射 。通过在变量声明中捕获这一附加的类型信息, 泛型 允许编译器实施这些附加的类型约束。类型错误现在就可以在编译时被捕获了,而不是在运行时当作 ClassCastException 展示出来。将类型检查从运行时挪到编译时有助于您更容易找到错误,并可提高程序的可靠性。

消除强制类型转换。   泛型 的一个附带好处是,消除源代码中的许多强制类型转换。这使得代码更加可读,并且减少了出错机会。

尽管减少强制类型转换可以降低使用 泛型 类的代码的罗嗦程度,但是声明 泛型 变量会带来相应的罗嗦。比较下面两个代码例子。

该代码不使用 泛型

List li = new ArrayList();
li.put(new Integer(3));
Integer i = (Integer) li.get(0);
 

 

 

该代码使用 泛型

List<Integer> li = new ArrayList<Integer>();
li.put(new Integer(3));
Integer i = li.get(0);

 

在简单的程序中使用一次 泛型 变量不会降低罗嗦程度。但是对于多次使用 泛型 变量的大型程序来说,则可以累积起来降低罗嗦程度。

潜在的性能收益。   泛型 为较大的优化带来可能。在 泛型 的初始实现中,编译器将强制类型转换(没有 泛型 的话,程序员会指定这些强制类型转换)插入生成的字节码中。但是更多类型信息可用于编译器这一事实,为未来版本的 JVM 的优化带来可能。

由于 泛型 的实现方式,支持 泛型 (几乎)不需要 JVM 或类文件更改。所有工作都在编译器中完成,编译器生成类似于没有 泛型 (和强制类型转换)时所写的代码,只是更能确保类型安全而已。


泛型 用法的例子

泛型 的许多最佳例子都来自集合框架,因为 泛型 让您在保存在集合中的元素上指定类型约束。考虑这个使用 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 = new HashMap<String, String>();
m.put("key", "blarg");
String s = m.get("key");
 

 

当使用 Map 泛型 化版本时,您不再需要将 Map.get() 的结果强制类型转换为 String ,因为编译器知道 get() 将返回一个 String

在使用 泛型 的版本中并没有减少键盘录入;实际上,比使用强制类型转换的版本需要做更多键入。使用 泛型 只是带来了附加的类型安全。因为编译器知道关于您将放进 Map 中的键和值的类型的更多信息,所以类型检查从执行时挪到了编译时,这会提高可靠性并加快开发速度。


向后兼容

  Java   语言中引入 泛型 的一个重要目标就是维护向后兼容。尽管 JDK 5.0 的标准类库中的许多类,比如集合框架,都已经 泛型 化了,但是使用集合类(比如 HashMap ArrayList )的现有代码将继续不加修改地在 JDK 5.0 中工作。当然,没有利用 泛型 的现有代码将不会赢得 泛型 的类型安全好处。

 


  泛型 基础

类型参数

在定义 泛型 类或声明 泛型 类的变量时,使用尖括号来指定形式类型参数。形式类型参数与实际类型参数之间的关系类似于形式方法参数与实际方法参数之间的关系,只是类型参数表示类型,而不是表示值。

泛型 类中的类型参数几乎可以用于任何可以使用类名的地方。例如,下面是   java .util.Map 接口的定义的摘录:

public interface Map<K, V> {
public void put(K key, V value);
public V get(K key);
}
 

 

Map 接口是由两个类型参数化的,这两个类型是键类型 K 和值类型 V 。(不使用 泛型 )将会接受或返回 Object 的方法现在在它们的方法签名中使用 K V ,指示附加的类型约束位于 Map 的规格说明之下。

当声明或者实例化一个 泛型 的对象时,必须指定类型参数的值:

Map<String, String> map = new HashMap<String, String>();
 

 

注意,在本例中,必须指定两次类型参数。一次是在声明变量 map 的类型时,另一次是在选择 HashMap 类的参数化以便可以实例化正确类型的一个实例时。

编译器在遇到一个 Map<String, String> 类型的变量时,知道 K V 现在被绑定为 String ,因此它知道在这样的变量上调用 Map.get() 将会得到 String 类型。

除了异常类型、枚举或匿名内部类以外,任何类都可以具有类型参数。


命名类型参数

推荐的命名约定是使用大写的单个字母名称作为类型参数。这与 C++ 约定有所不同(参阅 附录 A :与 C++ 模板的比较),并反映了大多数 泛型 类将具有少量类型参数的假定。对于常见的 泛型 模式,推荐的名称是:

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 ,因而一个 In

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值