java 泛型

来源:互联网 发布:template.js教程 编辑:程序博客网 时间:2024/06/16 10:37

用泛型类型来解决问题!

要消除如上所述的数据类型转换,有一种普遍的方法,就是用 泛型类型来增大 Java类型系统。可以将泛型类型看作是类型“函数”;它们通过类型变量进行参数化,这些类型变量可以根据上下文用各种类型参数进行实例化

例如,与简单地定义类 Hashtable 不同,我们可以定义泛型类Hashtable<Key, Value>,其中 KeyValue是类型参数。除了类名后跟着尖括号括起来的一系列类型参数声明之外,在 Tiger中定义这样的泛型类的语法和用于定义普通类的语法很相似。例如,可以按照如下所示的那样定义自己的泛型Hashtable 类:


清单 3. 定义泛型 Hashtable类
class Hashtable<Key, Value> { ... }


然后可以引用这些类型参数,就像我们在类定义主体内引用普通类型那样,如下所示:


清单 4.像引用普通类型那样引用类型参数
class Hashtable<Key, Value> {  ...  Value put(Key k, Value v) {...}  Value get(Key k) {...}}

类型参数的作用域就是相应类定义的主体部分(除了静态成员之外)(在下一篇文章中,我们将讨论为何 Tiger实现中有这样的“怪习”,即必须对静态成员进行此项限制。请留意!)。

创建一个新的 Hashtable 实例时,必须传递类型参数以指定 KeyValue 的类型。传递类型参数的方式取决于我们打算如何使用 Hashtable。在上面的示例中,我们真正想要做的是创建 Hashtable 实例,它只将Integer 映射为 String 。可以用新的Hashtable 类来完成这件事:


清单 5. 创建将 Integer 映射为 String的实例
import java.util.Hashtable;class Test {  public static void main(String[] args) {    Hashtable<Integer, String> h = new Hashtable<Integer, String>();    h.put(new Integer(0), "value");    ...  }}


现在不再需要数据类型转换了。请注意用来实例化泛型类 Hashtable的语法。就像泛型类的类型参数用尖括号括起来那样,泛型类型应用程序的参数也是用尖括号括起来的。


清单 6.除去不必要的数据类型转换
...String s = h.get("key");System.out.println(s);


当然,程序员若只是为了能使用泛型类型而必须重新定义所有的标准实用程序类(比如 HashtableList )的话,则可能会是一项浩大的工程。幸好,Tiger 为用户提供了所有 Java集合类的泛型版本,因此我们不必自己动手来重新定义它们了。此外,这些类能与旧代码和新的泛型代码一起无缝工作(下个月,我们会说明如何做到这一点)。





回页首

Tiger 的基本类型限制

Tiger 中类型变量的限制之一就是,它们必须用引用类型进行实例化 - 基本类型不起作用。因此,在上面这个示例中,无法完成创建从int 映射到 StringHashtable

这很遗憾,因为这意味着只要您想把基本类型用作泛型类型的参数,您就必须把它们组装为对象。另一方面,当前的这种情况是最糟的;您不能将int 作为键传递给 Hashtable ,因为所有的键都必须是Object 类型。

我们真正想看到的是,基本类型可以自动进行包装(boxing)和解包装(unboxing),类似于用 C#所进行的操作(或者比后者更好)。遗憾的是,Tiger 不打算包括基本类型的自动包装(但是人们可以一直期待 Java 1.6中出现该功能!)。





回页首

受限泛型

有时我们想限制可能出现的泛型类的类型实例化。在上面这个示例中,类 Hashtable的类型参数可以用我们想用的任何类型参数进行实例化,但是对于其它某些类,我们或许想将可能的类型参数集限定为给定类型范围内的子类型。

例如,我们可能想定义泛型 ScrollPane 类,它引用普通的带有滚动条功能的Pane 。被包含的 Pane 的运行时类型通常会是类Pane 的子类型,但是静态类型就只是 Pane

有时我们想用 getter 检索被包含的 Pane ,但是希望 getter的返回类型尽可能具体些。我们可能想将类型参数 MyPane 添加到ScrollPane 中,该类型参数可以用 Pane的任何子类进行实例化。然后可以用这种形式的子句: extends Bound 来说明MyPane 的声明,从而来设定 MyPane 的范围:


清单 7. 用 extends 子句来说明 MyPane声明
class ScrollPane<MyPane extends Pane> { ... }


当然,我们可以完全不使用显式的范围,只要能确保没有用不适当的类型来实例化类型参数。

为什么要自找麻烦在类型参数上设定范围呢?这里有两个原因。首先,范围使我们增加了静态类型检查功能。有了静态类型检查,就能保证泛型类型的每次实例化都符合所设定的范围。

其次,因为我们知道类型参数的每次实例化都是这个范围之内的子类,所以可以放心地调用类型参数实例出现在这个范围之内的任何方法。如果没有对参数设定显式的范围,那么缺省情况下范围是Object ,这意味着我们不能调用范围实例在 Object中未曾出现的任何方法。





回页首

多态方法

除了用类型参数对类进行参数化之外,用类型参数对方法进行参数化往往也同样很有用。泛型 Java编程用语中,用类型进行参数化的方法被称为 多态方法(Polymorphic method)

多态方法之所以有用,是因为有时候,在一些我们想执行的操作中,参数与返回值之间的类型相关性原本就是泛型的,但是这个泛型性质不依赖于任何类级的类型信息,而且对于各个方法调用都不相同。

例如,假定想将 factory 方法添加到 List类中。这个静态方法只带一个参数,也将是 List 唯一的元素(直到添加了其它元素)。因为我们希望 List成为其所包含的元素类型的泛型,所以希望静态 factory 方法带有类型变量 T这一参数并返回 List<T> 的实例。

但是我们确实希望该类型变量 T能在方法级别上进行声明,因为它会随每次单独的方法调用而发生改变(而且,正如我在下一篇文章中将讨论的那样,Tiger设计的“怪习”规定静态成员不在类级类型参数的范畴之内)。Tiger让我们通过将类型参数作为方法声明的前缀,从而在单独的方法级别上声明类型参数。例如,可以按照如下所示的那样为factory 方法 make 添加前缀:


清单 8.将类型参数作为前缀添加到方法声明
class Utilities {   <T extends Object> public static List<T> make(T first) {     return new List<T>(first);   }}


除了多态方法中所增加的灵活性之外,Tiger 中还增加了一个优点。Tiger使用类型推断机制,根据参数类型来自动推断出多态方法的类型。这可以大大减少方法调用的繁琐和复杂性。例如,如果想调用make 方法来构造包含 new Integer(0)List<Integer>新实例,那么只需编写:


清单 9. 强制 make构造新实例
Utilities.make(Integer(0))


然后会自动地从方法参数中推断出类型参数的实例化。

原创粉丝点击