Eclipse V3.1 中的 Java 泛型支持
Eclipse V3.1 中的 Java 泛型支持
级别: 中级
Neal Ford (nealford@nealford.com), 应用程序架构师, ThoughtWorks
2005 年 12 月 08 日
Java™ 5 提供泛型支持,泛型支持是开发人员多年以来所要求的特性。它代表了 Java 编程语言一次具有重要意义的升级。像泛型这么复杂的技术,不仅对工具供应商也对开发人员带来了挑战。本文着重介绍 Eclipse 如何应对泛型挑战以及泛型给 Java 语言带来的变化,展示了如何在 Eclipse 中充分利用泛型,包括对于快速帮助、快速修复、重构和项目参数选择的支持。此外,还展示了完全泛型化语言的一些微妙而重要的方面。
Java 中的泛型
几乎从第一个版本开始,Java 技术的创立者们就已经开始讨论对该语言添加泛型支持。C++ 通过标准模板库对泛型进行支持,但是由于缺少所有其他类(嵌入在 Java 语言中的 Object
类中)的一个统一父类,泛型的实现也受到阻碍。Java 编程语言的泛型支持是其历史上最重大的语法变化。由于某些显而易见的原因,工具支持比其他 SDK 升级的步法要慢得多。尽管如此,现在 Eclipse V3.1 已经对这些语言的新特性有了出色的支持。本文重点介绍其中的一些新特性。
Java 5 项目
为了打开 Eclipse V3.1 中的 Java 泛型支持,需要在机器上安装 Java 5,从一些平常的地方都可以下载到 Java 5。泛型支持连同项目属性一起出现在编译器设置页面。这意味着像以前一样,每个项目具有独立的 SDK 设置。为了创建使用泛型的项目,必须在创建项目时指定语言级别或者通过现有项目的项目属性指定语言级别。
Java 5 设置使用两个特定的属性页。第一个属性页指定编译器设置。
图 1. 针对 Java 5 支持的特定于编译器的设置
除非您已经在 Eclipse for Java 5 中设置了默认项目设置,否则需要为该项目覆盖那些设置。JDK compliance 区域允许您决定源文件和类文件的设置。当您把源文件设置为 5.0 级别时,就会获得很多新的内容帮助和重构选项。
另一个相关属性对话框是树型视图中的 Errors/Warnings 区域。
图 2. 项目属性的 Errors/Warnings 区域
大量 J2SE 5 选项能够控制 Eclipse 为您的 Java 5 代码产生什么类型的错误和警告(请参见表 1)
List
或 ArrayList
等类型上的操作,但没有指定类型。每当您使用一个保存有对象的旧式 Collection
类时就会产生一个警告。 Generic type parameter declared with a final type bound 编译器每当遇到一个涉及 final 类型的类型绑定时,就会发出一个错误或者警告。请看这个示例方法签名: public int doIt(List extends String> list)
因为 String
是 final 类型,参数不能扩展 String
,所以这样写比较有效:
public int doIt(List list)
Inexact type match for vararg
arguments 当编译器不能从 varargs
参数确定开发人员的意图时,它将生成一个警告。有一些与数组相关的 varargs
是不明确的。 Boxing and unboxing conversions 对自动装箱操作发出警告(装箱操作可能影响性能),并且不再对类型包装对象做对象身份的假设。这是一个默认状态下被忽略的小警告。 Missing @Override
annotation 应该为任何重写的方法包含 @Override
注释。缺少这个注释可能表示开发人员没有意识到该方法被重写。 Missing @Deprecated
annotation 由于缺少 @Deprecated
标志而产生的警告。 Annotation is used as super interface 您不能把 Deprecated
类作为超级接口。例如,不推荐这种写法: public interface BadForm extends Deprecated {
}
switch
switch
语句缺少枚举项意味着您可能遗漏一些枚举选项。 Unhandled warning tokens in @SuppressWarnings
Java 5 允许您添加注释以抑制编译器警告。如果您拼写错了一个警告或者使用了一个并不存在的警告,这个标志将发出一个警告。 Enable @SuppressWarnings
annotations 打开程序地(用代码)抑制您不关心的警告的能力。 一旦您根据喜好设定了所有的项目选项,就可以开始在 Eclipse 中使用泛型了。
从特定类型向泛型转换
请考虑清单 1 中的简单类,它创建了一个 Employee
和 Manager
对象的列表(Manager
扩展自 Employee
),将他们打印出来,给他们涨工资后再打印出来。
清单 1. HR 类
package com.nealford.devworks.generics.generics; import java.util.ArrayList; import java.util.Collections; import java.util.List; public class HR { public HR() { List empList = new ArrayList(5); empList.add(new Employee("Homer", 200.0, 1995)); empList.add(new Employee("Lenny", 300.0, 2000)); empList.add(new Employee("Waylon", 700.0, 1965)); empList.add(new Manager("Monty", 2000.0, 1933, (Employee) empList.get(2))); printEmployees(empList); System.out.println("----- Give everyone a raise -----"); for (int i = 0; i < empList.size(); i++) ((Employee) empList.get(i)).applyRaise(5); printEmployees(empList); System.out.println("The maximum salary for any employee is "+ Employee.MAX_SALARY); System.out.println("Sort employees by salary"); Collections.sort(empList); printEmployees(empList); System.out.println("Sort employees by name"); Collections.sort(empList, new Employee.NameComparer()); printEmployees(empList); System.out.println("Sort employees by hire year"); Collections.sort(empList, Employee.getHireYearComparator()); printEmployees(empList); } public void printEmployees(List emps) { for (int i = 0; i < emps.size(); i++) System.out.println(emps.get(i)); } public static void main(String[] args) { new HR(); } }
如果您打开了 Java 5 支持,编译这段代码会出现多种警告信息。
快速修复特性
每当 Eclipse 要给您的代码建议一种改进时,Eclipse 的快速修复特性就显示为编辑器窗口左侧边栏上的一个灯泡。在清单 1 中的代码中,您将会看到多个快速修复。
图 3. 快速修复灯泡指示您的代码待改进
快速修复使用灯泡和黄色波浪线指示待改进处。如果将鼠标移动至黄色波浪线上,可以看到出现在图 4 中的改进建议。
图 4. 快速修复指示什么应该被通用化
这里所列的快速修复建议只有一条建议。边上的灯泡提出建议,添加一个本地变量保存 List
的 add()
方法的返回值。然而,在这里该方法返回一个布尔类型值,并且被忽略了。
为了定位快速修复建议,移至重构菜单。Eclipse 中很多重构与 Java 5 中的泛型直接相关。“Infer Generic Type Arguments”重构将给列表增加泛型支持。 第一个对话框允许您选择选项。
图 5. Infer Generic Type Arguments choices 对话框
第一个选项与一个结论相关,这个结论是 clone()
方法将返回接收者类型而不是另外一个类型(相关类)。大部分功能良好的类都遵守这个规则,如果您知道您的类不遵守这个规则,则不要选中这个选项。当第二个选项未选中时,将保留“raw”(非泛型)参数,而不是推断出正确的泛型参数类型。
Eclipse 中的大多数重构中,您都可以预览您的类将发生什么变化。点击这个对话框上的 Preview 按钮将出现图 6 所示的对话框。
图 6. Preview the generic refactoring
更新后的代码如下:
清单 2. 更新后的代码
ListempList = new ArrayList (5); empList.add(new Employee("Homer", 200.0, 1995)); empList.add(new Employee("Lenny", 300.0, 2000)); empList.add(new Employee("Waylon", 700.0, 1965)); empList.add(new Manager("Monty", 2000.0, 1933, empList.get(2)));
代码发生了两个有趣的变化。第一 —— 也是最明显的 —— List
和 ArrayList
声明现在是 Employee
类型的泛型。第二 —— 不太明显 —— 代码最后一行发生的变化。您观察一下 Manager
类的原来的 empList
添加,它的最后一个参数需要针对 Assistant
域强制类型转换为 Employee
。而 Infer 重构足够聪明,它可以删除现在不必要的类型强制转换。
在介绍完快速修复之前,Eclipse 还在 Java 5 支持中增加了另外一个有趣的方面:您可以得到为方法添加注释的建议,比如 @Override
。您还具有针对注释的内容帮助。
图 7. 针对注释的快速修复和内容帮助扩展
快速帮助特性
Eclipse V3.1 已经添加了快速帮助以促进 Java 5 中的泛型支持。请考虑这个普通的 for()
循环,参见清单 3 中的 printEmployees()
方法。
清单 3. for() 循环
public void printEmployees(Listemps) { for (int i = 0; i < emps.size(); i++) System.out.println(emps.get(i)); }
除了对泛型的支持外,Java 5 现在也支持 for...each 循环
。快速帮助建议将 for loop
变成 for...each
,变化后的代码如清单 4 所示。
清单 4. for...each 循环
public void printEmployees(Listemps) { for (Employee emp : emps) System.out.println(emp); }
这个版本由于完全删除了 i
变量和 get()
方法调用而变得清洁多了。
泛型类型
Eclipse V3.1 为了扩展到泛型类型而扩大了对类型操作的支持。这意味着:
- 泛型类型能够被安全地重命名。
- 类型变量能够被安全地重命名。
- 泛型方法能够从泛型代码中抽象出来或者嵌入泛型代码。
- 代码帮助可以自动将合适的类型参数插入参数化的类型中。
Eclipse 中的搜索工具对于泛型类型已经具有了更高的智能性。请考虑如下代码:
清单 5. 泛型类型
public void doEmployeeAnalysis() { ListempList = new ArrayList (5); List hireDates = new ArrayList (5); List departments = new ArrayList (10); List extends Employee> allMgrs = new ArrayList (5); . . .
如果您选中第一个 List
声明并且选择 Search > References > Project,Eclipse 将显示给您所有的 list 声明。
图 8. Eclipse 在寻找泛型引用方面已经变得聪明
您也可以通过 Search 窗口隐藏良好的特性来过滤这些结果。如果您访问 Search 窗口菜单(在右上角,最小化和最大化按钮的旁边),您可以找到泛型感知的过滤选项。
图 9. 搜索窗口的过滤菜单允许您过滤泛型感知的结果
如果您使用 Filter Incompatible 过滤结果,将删除两个不是基于 Employee
的条目。结果如图 10 所示。
图 10. Filter Incompatible 在搜索窗口过滤掉与非 Employee 相关的条目
深入了解泛型
不幸的是,Eclipse 不能解决您所有的泛型问题。事实上,有时重构会为您要解决的问题产生语法正确但是语义不正确的代码。具有讽刺意味的是,在泛型出现之前的那些日子更轻松,因为您必须将所有东西都作为对象的泛型集合传递。而现在您必须小心地传递正确类型的集合。
考虑这个例子。在 HR 应用程序中,您添加一个方法确定雇员的退休年龄。然而,Employee
的年龄是来自于 Employee
的父类:Person
。写一个方法只接受在这个实例中工作的雇员,但是您不想将您的应用程序只用于雇员。如果将来您需要查询以前的雇员(作为 Persons
),该怎么办呢?
这个问题的解决方案在于灵活的泛型参数。请考虑清单 6 中的代码,它接受任何扩展自 Person
的类。
清单 6. 示例 SELECT 语句
public ListempsOverRetirementAge( List extends Person> people) { List retirees = new ArrayList (1); for (Person e : people) if (e.getAge() > 65) retirees.add(e); return retirees; }
该方法接受 Person
的任何子类,所以更灵活。使用泛型的时候,您应该牢记这一点。在本例中,泛型实际上比较特定(至少,他们应该称这种语言特性为“特定性”)。仔细识别参数类型能够使您的代码获得同样的灵活性,因此性能比泛型更好,但是具有泛型提供的附加的类型安全性。
结束语
泛型支持大大增强了 Java 编程语言,工具供应商必然需要很长时间才能赶上。现在有了好的工具支持,您应该开始利用这种高级语言特性。它使代码更加可读 —— 因为删除了类型强制转换 —— 并且允许编译器为您做更多的工作。任何时候您都可以让编译器和其他的工具(如 IDE)做更多的工作,这意味着您要做的工作更少。
参考资料
学习- 您可以参阅本文在 developerWorks 全球站点上的 英文原文
- 从这里开始查找 Eclipse 项目参考资料。
- 阅读“Eclipse 平台入门”,获得 Eclipse 概述,包括它的起源和架构。
- “AOP@Work: AOP 工具比较,第 1 部分”研究 AOP 框架,该框架支持 Java 编程语言中的泛型。
- developerWorks 已经发表了许多 Eclipse 文章和教程。
- 如果您刚接触泛型,请阅读 Generics in the Java Programming Language,其中概述了 Java 5 中发生的变化。
- Java Pro Magazine 上的一篇关于泛型概述的好文章,阅读 Language Features of Java Generics。
- 阅读 Quiz: Java 1.5 Generics。它将测试您关于 Java 5 中泛型支持的知识 —— 并且它是消磨时间的好办法。
- 访问 developerWorks 开放源码专区,获得更多的 how-to 信息、工具和项目更新,帮助您使用开放源码技术进行开发,并与 IBM 产品一起使用。
获得产品和技术
- 使用 IBM 测试软件 改进您的下一个开放源码开发项目,可以通过下载或从 DVD 中获得这些软件。
讨论
- 通过参与 developerWorks blogs 加入 developerWorks 社区。
关于作者
Neal Ford 是 ThoughtWorks 的应用程序架构师,ThoughtWorks 是一个 IT 专业服务公司。他还是应用程序、教学材料、杂志文章、课件、视频/DVD 演示文稿的设计者和开发者,还是 Developing with Delphi: Object-Oriented Techniques、JBuilder 3 Unleashed 和 Art of Java Web Development 等书籍的作者。他主要是构建大型企业应用程序方面的顾问。他还在许多开发人员研讨会上做过演讲。