`

深入研究枚举类型(Java 5.0) 更有效地处理常量

阅读更多
John Zukowski (jaz@zukowski.net), 总裁, JZ Ventures, Inc.

2005 年 4 月 19 日

    2004 年 11 月,Brett McLaughlin 带您初步了解了如何在 Java 5.0 平台上使用枚举类型。在这个月的“驯服 Tiger”技巧文章中,专栏作家 John Zukowski 将解释如何使用枚举类及其预定义方法,同时还将展示如何添加构造函数、覆盖方法,以及拥有实例变量。请在本文附带的 讨论论坛 中与作者和其他读者分享您对本文的看法。(也可以单击本文顶部或底部的 讨论 来访问该论坛。)

基础知识

正如 Brett McLaughlin 在他的文章“枚举类型入门”中所描述的那样(推荐您先阅读这篇文章),通过使用新的 enum 关键字创建指定的对象集合,您可以创建一个枚举类型。然后,可以将每个指定的值看作是那个类的一个实例,这为您提供了指定的整数集合所无法提供的编译时类型安全。清单 1 将创建一个枚举类型,并将类型安全的枚举值作为帮助器方法(helper method)的参数。该枚举类型的 values() 方法返回这种类型的不同值的有序数组。

清单 1. 枚举类型的例子

public class Loop {
  enum Size {
    Small,
    Medium,
    Large
  }
  public static void main(String args[]) {
    for (Size s : Size.values()) {
      helper(s);
    }
  }
  private static void helper(Size s) {
    System.out.println("Size value: " + s);
  }
}


构造函数、方法和变量

在使用 enum 关键字创建新的枚举类型时,实际上是在创建 java.lang.Enum 类的子类,其中,枚举类型符合通用模式 Class Enum<E extends Enum<E>>,而 E 表示枚举类型的名称。枚举类型的每一个值都将映射到 protected Enum(String name, int ordinal) 构造函数中,在这里,每个值的名称都被转换成一个字符串,并且序数设置表示了每个设置的优先值。换句话说,enum Size {Small, Medium, Large} 将映射到清单 2 中所示的构造函数调用中:

清单 2. 映射的构造函数调用

  new Enum<Size>("Small", 0);
  new Enum<Size>("Medium", 1);
  new Enum<Size>("Large", 2);


不必将构造函数的使用限制为间接 Enum 构造函数调用。在使用 enum 关键字时,将创建 Enum 的子类。您可以使用参数和任何别的东西为定义的每个名称添加一些您自己的构造函数调用。名称声明可以看作是对构造函数的调用,您不必添加 new 关键字。这种方法允许您将数据作为参数值传递给构造函数调用,如清单 3 所示。该参数表示 Size 对象的枚举集合的定价因子。位于枚举类型定义之后的 main() 方法演示了这种用法。

清单 3. 定制构造函数的例子

public class Sample {
  enum Size {
    Small(0.8),
    Medium(1.0),
    Large(1.2);
    double pricingFactor;
    Size(double p) {
      pricingFactor = p;
    }
  }
  public static void main(String args[]) {
    Size s = Size.Large;
    double d = s.pricingFactor;
    System.out.println(s + " Size has pricing factor of " + d);
  }
}


运行该程序将返回给定 Size 的定价因子。您还可以定义一个类似于 getPricingFactor() 的方法,并将 pricingFactor 字段设置为 private,以便更多地将它作为类 JavaBean 的属性对待。清单 4 给前面的例子添加了一个方法:

清单 4. 方法的例子

public class Sample2 {
  enum Size {
    Small(0.8),
    Medium(1.0),
    Large(1.2);
    private double pricingFactor;
    Size(double p) {
      pricingFactor = p;
    }
    public double getPricingFactor() {
      return pricingFactor;
    }
  }
  public static void main(String args[]) {
    Size s = Size.Large;
    double d = s.getPricingFactor();
    System.out.println(s + " Size has pricing factor of " + d);
  }
}


对于这两种情况,输出均为:

Large Size has pricing factor of 1.2



预定义的方法

因为用户定义的枚举类型是 Enum 类型的子类,所以您需要继承用于您的类型的那个类的所有方法。下面列出了完整的方法集合(E 表示枚举类型自身):

    * public int compareTo(E e)
    * public boolean equals(Object o)
    * public final Class<E> getDeclaringClass()
    * public int hashCode()
    * public String name()
    * public int ordinal()
    * public String toString()
    * public static <T extends Enum<T>> T valueOf(Class<T> enumType, String name)

一些方法看起来很熟悉,而其他一些方法则是特定于 Enum 类的。compareTo()、equals() 和 hashCode() 方法是典型的 Object 和 Comparable 方法,其中,compareTo() 报告声明元素的顺序。name() 和 ordinal() 方法返回构造函数参数,而 toString() 返回名称。

getDeclaringClass() 和 valueOf() 方法需要稍多一些解释。getDeclaringClass() 方法类似于 Object 的 getClass() 方法,但它没必要返回相同的类。根据这个方法的 Javadoc 的说明:

    对于具有特定于常量的类主体的 enum 常量,该方法返回的值可能不同于 Object.getClass() 方法返回的值。

接下来,我将解释特定于常量的类主体。valueOf() 方法是静态的,它允许您从类型的名称中创建枚举的值。



特定于常量的类主体

特定于常量的类主体是 enum 关键字的一个受支持的特性;不过,它们的使用应该受到严格的限制。这个概念正在深入到将枚举类型的每个元素作为一个子类对待的领域。例如,在前面的例子中,Size 枚举类型有一个定价因子参数和 getPricingFactor() 方法。但没有构造函数参数,清单 5 展示了如何利用特定于常量的主体来做同样的事。我们添加了一些额外的大小来让这个例子更有趣些。在这里,Small 的定价因子是 0.8,而 ExtraLarge 和 ExtraExtraLarge 的定价因子是 1.2。其余的大小则采用默认值,即 1.0。

清单 5. 特定于常量的主体

public class Sample3 {
  enum Size {
    Small {
      public double getPricingFactor() {
        return 0.8;
      }
    },
    Medium,
    Large,
    ExtraLarge {
      public double getPricingFactor() {
        return 1.2;
      }
    },
    ExtraExtraLarge {
      public double getPricingFactor() {
        return 1.2;
      }
    };
    public double getPricingFactor() {
      return 1.0;
    }
  }
  public static void main(String args[]) {
    for (Size s : Size.values()) {
      double d = s.getPricingFactor();
      System.out.println(s + " Size has pricing factor of " + d);
    }
  }
}


如果回头想想前面描述过的 getDeclaringClass() 方法,您就能明白为什么这些特定于常量的主体和 getClass() 能够在拥有特定于常量的类主体的同时返回不同的类。



EnumMap 和 EnumSet

java.util 程序包中包含两个类:EnumMap 和 EnumSet,这两个类有助于使处理枚举类型变得更容易一些。EnumMap 类提供了 java.util.Map 接口的一个特殊实现,该接口中的键(key)是一个枚举类型。EnumSet 类提供了 java.util.Set 接口的实现,该接口保存了某种枚举类型的值的集合。

清单 6 展示了 EnumMap 类的用法。在创建映射时,必须为枚举的键传入这个类。

清单 6. EnumMap 的例子

import java.util.*;
public class EnumMapSample {
  enum Size {
    Small,
    Medium,
    Large;
  }
  public static void main(String args[]) {
    Map<Size, Double> map = new EnumMap<Size, Double>(Size.class);
    map.put(Size.Small, 0.8);
    map.put(Size.Medium, 1.0);
    map.put(Size.Large, 1.2);
    for (Map.Entry<Size, Double> entry : map.entrySet()) {
      helper(entry);
    }
  }
  private static void helper(Map.Entry<Size, Double> entry) {
    System.out.println("Map entry: " + entry);
  }
}


枚举集合的作用类似于特性的集合,或者类似于某个枚举类型的所有元素的值的子集。EnumSet 类拥有以下一系列的静态方法,可以用这些方法从枚举类型中获取单个元素:

    * public static <E extends Enum<E>> EnumSet<E> allOf(Class<E> elementType)
    * public static <E extends Enum<E>> EnumSet<E> complementOf(EnumSet<E> s)
    * public static <E extends Enum<E>> EnumSet<E> copyOf(Collection<E> c)
    * public static <E extends Enum<E>> EnumSet<E> noneOf(Class<E> elementType)
    * public static <E extends Enum<E>> EnumSet<E> of(E e)
    * public static <E extends Enum<E>> EnumSet<E> of(E first, E... rest)
    * public static <E extends Enum<E>> EnumSet<E> of(E e1, E e2)
    * public static <E extends Enum<E>> EnumSet<E> of(E e1, E e2, E e3)
    * public static <E extends Enum<E>> EnumSet<E> of(E e1, E e2, E e3, E e4)
    * public static <E extends Enum<E>> EnumSet<E> of(E e1, E e2, E e3, E e4, E e5)
    * public static <E extends Enum<E>> EnumSet<E> range(E from, E to)

一旦创建了 EnumSet,就可以像对待其他任何 Set 对象那样对待这组对象。



结束语

使用枚举类型的基本概念很简单。您可以先定义一个指定的、封闭的值集合。然后,在需要这些值中的某一个值时,可以通过它的名称来指定它。该名称携带为其设置的类型。对于不同的大小,不是说 1 = Small, 2 = Medium, 3 = Large,并且确保没有将 1 = Monday 的这类东西传递给期望获得一个 Size 的方法,而是传入 Small、Medium 或 Large 作为 Size,因为编译器会确保您没有传入 Monday。这就是枚举类型的简单性。这些枚举类型就是类本身,因此,可以对类进行的所有操作同样可以作用于枚举类型上。

此外,枚举类型支持拥有构造函数、实例方法和变量,等等。应该对枚举类型使用这些方面吗?尽管使用这些方法和新的支持类肯定没问题,但提供构造函数和覆盖方法会有问题。说出为枚举中的每个 Size 所支付的价格真的有意义吗?或者说,在一个拥有枚举类型 Size 变量的类中做这些更有意义吗?

小心使用这些特性,不要因为这些特性可用就使用它们。要考虑到系统的总体设计,不要只图快点完成工作。


参考资料

    * 您可以参阅本文在 developerWorks 全球站点上的 英文原文。

    * 请参阅完整的 驯服 Tiger 系列。

    * 从 Sun Developer Network 下载 J2SE 5.0。

    * Brett McLaughlin 撰写的文章 “枚举类型入门”(developerWorks, 2004 年 11 月),研究了如何使用 Java 5.0 平台以类型安全的方式表示常量。这是一篇必读文章。

    * “用 for/in 在 Java 5.0 中增强循环”(developerWorks, 2004 年 11 月)的作者也是 Brett McLaughlin,这篇文章有助于您发现这个便利的构造提供了哪些特性,以及什么时候它最适用于您的项目。

    * 请参阅 Enum、 EnumMap 和 EnumSet 类的 Javadocs。

    * 请参阅 Sun 的 J2SE 5.0 文档中的 type-safe enums。

    * 要了解关于 Java 技术的更多信息,请参阅 developerWorks Java 专区。您将在那里找到技术文档、how-to 文章、教育、下载、产品信息和其他更多信息。

    * 请参阅 New to Java technology 站点,以获得帮助您初步了解 Java 编程的最新参考资料。

    * 通过参与 developerWorks blogs 加入 developerWorks 社区。

    * 从 Developer Bookstore 购买 打折出售的 Java 书籍。



关于作者

John Zukowski's Photo


John Zukowski 负责 JZ Ventures, Inc. 公司的战略 Java 咨询,与 SavaJe Technologies 一起开发下一代移动电话平台。他的新著是 The Definitive Guide to Java Swing, Third Edition(Apress, 2005 年 6 月)和 Mastering Java 2, J2SE 1.4(Sybex, 2002 年 4 月)。
分享到:
评论

相关推荐

    Java API_5.0中文版

    以上只是Java API 5.0中的一部分核心知识点,实际的学习过程中,还需要深入研究每个类和接口的功能,熟悉它们的API用法,并通过实践来加深理解。阅读Java API文档是提升编程技能和解决问题的重要途径。

    良葛格Java JDK 5.0学习笔记

    通过良葛格的Java JDK 5.0学习笔记,你可以系统地了解和掌握这些特性,提升自己的Java编程技能,无论是进行基础学习还是深入研究,都是不可或缺的参考资料。这份笔记将引导你领略Java 5.0的魅力,使你在编程道路上...

    (Java 2 SDK)JDK 5.0 的源代码

    在JDK 5.0之前,Java没有内置的枚举类型,开发者通常使用常量来模拟枚举。但JDK 5.0引入了枚举,使得枚举变量成为一种正式的类型,可以拥有方法和实现接口,大大增强了枚举的功能性和可读性。 三、自动装箱与拆箱...

    《Java5.0 Tiger》书籍

    2. **枚举类型**:Java 5.0引入了枚举类型,这是一种预定义的类,用于表示一组固定数量的常量。枚举提高了代码的可读性和安全性,防止了未定义的枚举值的使用。 3. **自动装箱与拆箱**:自动装箱和拆箱使得基本类型...

    src_java_Tiger_

    1. **枚举类型(Enums)**:Java 5.0 引入了枚举,提供了一种更加安全和灵活的方式来定义常量集合。在源代码中,你可以找到如何创建枚举类,以及如何在枚举中定义方法和实现接口的例子。 2. **泛型(Generics)**:...

    JAVASE --api

    JavaSE(Java Standard Edition)是Java开发平台的标准版,它为构建桌面应用程序提供了核心功能。...通过深入研究这个文档,开发者可以更好地掌握Java 5.0及其后续版本的编程技巧,提高代码的效率和可维护性。

    java开发帮助文件API帮助大全

    Java开发帮助文件API是...通过深入研究这些文件,开发者可以提升编程技能,更有效地解决问题,编写出高效、可靠的Java应用程序。同时,了解和掌握这些API将有助于开发者遵循最佳实践,提高代码的可读性和可维护性。

    HTML的java帮助文档

    HTML的Java帮助文档,主要指的是Java 5.0版本的API(Application Programming Interface)文档,以HTML格式编译,...通过深入研究这个文档,不仅可以掌握Java 5.0的最新特性,也能提升自己的编程技巧和解决问题的能力。

    JAVA API1.5中文帮助文档

    Java API 1.5中文帮助文档是学习Java编程的...通过深入研究这份文档,开发者能够全面掌握Java 1.5的新特性,从而编写出更加高效、安全的代码。无论是为了学习基础,还是解决复杂问题,这部文档都是不可或缺的参考资料。

    JDK5.0新特性源代码

    枚举类型比传统的整数常量或字符串常量更便于管理和使用。例如,`enum Color {RED, GREEN, BLUE}`定义了一个颜色的枚举。 3. **自动装箱/拆箱(Autoboxing and Unboxing)**:自动装箱允许基本数据类型(如int、...

    java API 帮助文档

    2. **枚举类型**:Java 5之前的版本中,枚举通常是通过常量定义的,而在Java 5中,枚举成为了一种完全的类型,可以包含方法、字段和构造函数,提供了更强大的枚举支持。 3. **自动装箱与拆箱**:Java 5开始支持基本...

    jdk5.0源代码下载

    - **枚举**:新增的枚举类型使得常量的表示更加规范,支持方法和继承,解决了常量类的局限性。 - **自动装箱与拆箱**:自动装箱允许基本类型与对应的包装类之间无缝转换,简化了编程。 - **增强的for循环**...

    jdk-1_5_0_22-linux-amd64

    2. **枚举**:枚举类型是预定义的一组常量,提供了更好的类型安全性和代码可读性,取代了以前的常量整数或字符串表示。 3. **自动装箱/拆箱**:自动装箱允许在原始类型和对应的包装类之间无缝转换,减少了手动转换的...

    jdk帮助文档中文 1.5, 1.6 ,1.8

    Java JDK(Java Development Kit)是Java编程语言的核心组件,它包含了一个Java运行环境、编译器(javac)、...通过深入研究这些文档,开发者可以更好地理解和利用Java平台提供的各种功能,实现高效、可靠的软件开发。

    api.rar_JavaTM 2 Platform

    Java TM 2 Platform Standard Edition(通常简称为J2SE)5.0是Java开发和运行环境的一个重要版本,它...通过深入研究这些知识点,开发者可以更好地利用Java的强类型和面向对象特性,编写出高效、安全且易于维护的代码。

    中文版JDK 1.5 API文档

    2. **枚举(Enums)**: 枚举类型是一种特殊的类,用于定义固定的常量集合,如方向(North, South, East, West)或星期(Monday, Tuesday...)。枚举提高了代码的可读性和安全性。 3. **自动装箱与拆箱(Autoboxing/...

    jdk1.5中帮助文档

    **Java SDK 1.5 帮助文档详解** Java SDK 1.5,也称为Java 5.0,是Java发展历程中的一个重要里程碑。...通过深入研究这些文档,不仅可以掌握Java 1.5的新特性,还能更好地理解和利用Java平台提供的各种功能。

    jdk1.5CN.CHM

    《Java Development Kit 1.5 中文版 CHM 手册》 JDK 1.5,也称为 Java SE 5.0,是Java平台的...通过深入研究这个手册,开发者可以提升编程技能,充分利用JDK 1.5提供的新功能,编写出更高效、更健壮的Java应用程序。

    j2se5.rar_J2SE 架构_j2se

    Java 2 Platform, Standard Edition(J2SE)是Java开发平台的一个重要组成部分,主要针对桌面应用和服务器端应用的...通过深入研究J2SE 5.0的新特性,你可以更好地适应Java编程的现代需求,提高代码质量和可维护性。

    J2SE6.0 冷冬大雪&英文版

    增强的for循环简化了遍历集合的操作,泛型提高了类型安全,枚举为常量提供了更好的表示,而注解则为元数据提供了支持,广泛应用于框架和库的配置。 在实际开发中,熟练掌握J2SE6.0的特性,能够提升代码质量,减少...

Global site tag (gtag.js) - Google Analytics