关于java:java泛型

8次阅读

共计 3280 个字符,预计需要花费 9 分钟才能阅读完成。

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 —— 泛型。

正文完
 0