技术开发 频道

使用泛型和并发改善集合

  【IT168 技术文章】Java™ Collections Framework 是 Java 平台的一个重要部分。桌面和企业应用程序通常都使用该框架来聚集集合项。本文将向您展示如何使用集合,同时利用 Java SE 6 中对该框架的增强。通过使用泛型和并发功能使您的应用程序具有更好的维护性和可伸缩性,您可以实现比 HashMap 或 TreeSet 更丰富的功能。

  了解泛型

  从 Java SE 5 版本开始,泛型的概念就成为了 Java 平台的一部分。简单来说,泛型为集合提供了编译时类型安全。在早期的 Java 平台版本中,您创建一个集合,并向其中添加项,如清单 1 所示:

  清单 1. 向集合添加项 — 旧方法

1 List buttonList = new LinkedList();
2 buttonList.add(new JButton("One"));
3 buttonList.add(new JButton("Two"));
4 buttonList.add(new JButton("Three"));
5 buttonList.add(new JButton("Four"));

  要从集合中提取元素,您必须知道集合中对象的类型,以将其强制转换为合适的局部变量:

1 JButton first = (JButton)buttonList.get(0);
2

  您并不需要 将其强制转换为正确的类型,但是如果您想要对某个特定类类型进行操作,则需要这么做。这种方法运行得很好,除非您不小心向集合中添加了错误的类型对象:

1 buttonList.add(new JLabel("Five"));
2

  现在,如果您尝试将最后一个元素作为 JButton 来提取,则在运行时会出现一个类转换异常:

1 Line 13: JButton last = (JButton)buttonList.get(4);
2 >java GetIt
3 Exception in thread "main" java.lang.ClassCastException:
4   javax.swing.JLabel cannot be cast to javax.swing.JButton
5         at GetIt.main(GetIt.java:13)
6

  在本质上,将 JLabel 放入集合并没有任何问题,但是如果提取代码希望集合中的所有元素都是同一类型(这里为 JButton),那么从集合中提取一个 JLabel 就会生成 ClassCastException。这个异常只会在运行时出现;如果没有进行足够的测试,那么也许直到部署之后才会出现该异常。

  泛型集合的使用

  现在进入泛型的世界。泛型可以帮助您在开发周期的早期解决编码问题。不只是拥有一个集合并向其中添加 JButton 对象,您可以拥有一个 JButton 对象的集合。然后,如果想要将 JLabel 添加到集合,则编译器会在编译时发现差异和并抛出异常。

  在尝试向泛型集合(本例中为 List)添加错误类型的元素时,清单 2 中的程序会生成编译时错误消息:

  清单 2. 使用泛型的示例代码(没有编译)

1 import java.util.*;
2 import javax.swing.*;
3
4 public class GetIt {
5   public static void main(String args[]) {
6     List<JButton> buttonList = new LinkedList<JButton>();
7     buttonList.add(new JButton("One"));
8     buttonList.add(new JButton("Two"));
9     buttonList.add(new JButton("Three"));
10     buttonList.add(new JButton("Four"));
11     JButton first = buttonList.get(0);
12     buttonList.add(new JLabel("Five"));
13     JButton last = buttonList.get(4);
14   }
15 }
16

  当您保存并编译该应用程序时,您将注意到对 add() 的最后调用失败了:

1 >javac GetIt.java
2 GetIt.java:12: cannot find symbol
3 symbol  : method add(javax.swing.JLabel)
4 location: interface java.util.List<javax.swing.JButton>
5                 buttonList.add(new JLabel("Five"));
6                           ^
7 1 error
8

  错误消息的第二行表明,您尝试将一个 JLabel 添加到第三个错误行报告的 JButton 对象的 List。然后您必须决定该集合是否必须为 Component 对象(或者 JComponent,如果您想要使用 Swing 平台组件)的集合,或者您是否不应该尝试在第一个位置添加 JLabel。

  注意,在 清单 2 中,从集合中提取项并不需要将其强制转换为正确的类型。因为您已经说明了集合为某种类型,从集合中提取项的所有调用都返回给定的类型。

  泛型的使用使您的代码库更容易维护,尤其当代码库不断增长,以及将代码元素转换为可重复使用的库时。库的用户不用担心对集合中对象的类型有任何限制。正确定义的方法应该在其定义中包含这些类型。并且如果您的类型不符合该类型,编译器会给出警告。

  泛型编译器问题

  当一个类使用的集合的定义中缺少泛型类型时,在编译这个类时,编译器就会报错,比如编译 清单 1 中的代码就会出现这种情形。例如,假设您想要编译一个包含以下行的类:

1 List buttonList = new LinkedList();
2

  编译器会发出一个警告:

1 >javac GetIt.java
2 Note: GetIt.java uses unchecked or unsafe operations.
3 Note: Recompile with -Xlint:unchecked for details.
4

  您可以忽略该警告,不予理会。假设您并没有意外添加错误的数据类型到集合中,那么一切都会运行良好。

  查看详细的警告

  要了解编译器警告您的特定问题的详细信息,可以向编译器发出 -Xlint:unchecked 命令。您将看到如清单 3 所示的输出:

  清单 3. 使用 Xlint 进行编译的详细信息

1 >javac -Xlint:unchecked GetIt.java
2 GetIt.java:7: warning: [unchecked] unchecked call to add(E) as a member of
3         the raw type java.util.List
4     buttonList.add(new JButton("One"));
5                   ^
6 GetIt.java:8: warning: [unchecked] unchecked call to add(E) as a member of
7         the raw type java.util.List
8     buttonList.add(new JButton("Two"));
9                   ^
10 GetIt.java:9: warning: [unchecked] unchecked call to add(E) as a member of
11         the raw type java.util.List
12     buttonList.add(new JButton("Three"));
13                   ^
14 GetIt.java:10: warning: [unchecked] unchecked call to add(E) as a member of
15         the raw type java.util.List
16     buttonList.add(new JButton("Four"));
17                   ^
18 4 warnings
19

  在清单 3 中可以看到,编译器并不特别关心 List 未定义数据类型。它显示的是,每个对 add() 的调用都存在问题,因为该 List 未定义数据类型。

  另外,这些都是警告,所以您可以忽略它们。但是,修复该集合以显式地指定类型,将会避免在编译时遇到这些警告引起的一个真正的错误。

  禁止编译器警告

  如果您使用的是一个不能或不想更改的库,那么您可以禁止编译器警告。@SuppressWarnings 注释会告诉编译器您知道代码生成了警告,但是您不想看到它们。如果您将下面这行代码添加到想要忽略其警告的方法前面,则编译器不再显示该方法的警告:

1 @SuppressWarnings("unchecked")
2

  现在,当您编译该类时,将不会看到警告消息或错误消息。如果处理未预料到的数据类型,您仍然有可能获得 ClassCastException。选择权在您手中。

0
相关文章