`
qqdwll
  • 浏览: 136679 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

Java枚举类型

    博客分类:
  • Java
阅读更多
Tiger中的一个重要新特性是枚举构造,它是一种新的Java枚举类型,允许用常量来表示特定的数据片断,而且全部都以类型安全的形式来表示。它能够让我们抛弃所有旧的public static final 代码。

下面先看看枚举的一些简单例子, 下面就是一个典型枚举类型的定义:
  public enum Color{

    RED,BLUE,BLACK,YELLOW,GREEN

    }


显然,enum很像特殊的class,实际上enum声明定义的类型就是一个类。而这些类都是类库中Enum类的子类 (java.lang.Enum<E>)。它们继承了这个Enum中的许多有用的方法。下面我们就详细介绍enum定义的枚举类的特征及其用法。(后面均用Color举例)

1、Color枚举类是特殊的class,其枚举值(RED,BLUE...)是Color的类对象(类实例):Color c=Color.RED;而且这些枚举值都是public static final的,也就是我们经常所定义的常量方式,因此枚举类中的枚举值最好全部大写。

2、即然枚举类是class,当然在枚举类型中有构造器,方法和数据域。但是,枚举类的构造器有很大的不同:

(1) 构造器只是在构造枚举值的时候被调用。
    enum Color{
   //构造枚举值,比如RED(255,0,0)
    RED(255,0,0),BLUE(0,0,255),BLACK(0,0,0),YELLOW(255,255,0),GREEN(0,255,0); 
     private Color(int rv,int gv,int bv){ 
             this.redValue=rv; 
             this.greenValue=gv; 
             this.blueValue=bv;
    }

  //自定义的public方法
    public String toString(){ 
          return super.toString()+"("+redValue+","+greenValue+","+blueValue+")";
    }

   //自定义数据域,private为了封装。
    private int redValue;  

    private int greenValue;

    private int blueValue;

    }


(2) 构造器只能私有private,绝对不允许有public构造器。这样可以保证外部代码无法新构造枚举类的实例。这也是完全符合情理的,因为我们知道枚举值是public static final的常量而已。 但枚举类的方法和数据域可以允许外部访问。
public static void main(String args[])

    {

    // Color colors=new Color(100,200,300);  //wrong

    Color color=Color.RED;

    System.out.println(color);  // 调用了toString()方法

    }



  3、所有枚举类都继承了Enum的方法,下面我们详细介绍这些方法。

    (1)  ordinal()方法: 返回枚举值在枚举类种的顺序。这个顺序根据枚举值声明的顺序而定。

    Color.RED.ordinal();  //返回结果:0
    Color.BLUE.ordinal();  //返回结果:1

   (2)  compareTo()方法: Enum实现了java.lang.Comparable接口,因此可以比较象与指定对象的顺序。Enum中的compareTo返回的是两个枚举值的顺序之差。当然,前提是两个枚举值必须属于同一个枚举类,否则会抛出ClassCastException()异常。 
   Color.RED.compareTo(Color.BLUE);  //返回结果 -1

   (3)  values()方法: 静态方法,返回一个包含全部枚举值的数组。

    Color[] colors=Color.values();

    for(Color c:colors){

    System.out.print(c+","); }//返回结果:RED,BLUE,BLACK YELLOW,GREEN,
   (4)  toString()方法: 返回枚举常量的名称。

    Color c=Color.RED;
    System.out.println(c);//返回结果: RED
   (5)  valueOf()方法: 这个方法和toString方法是相对应的,返回带指定名称的指定枚举类型的枚举常量。

    Color.valueOf("BLUE");   //返回结果: Color.BLUE
(6)  equals()方法: 比较两个枚举类对象的引用。
public final boolean equals(Object other) {

    return this==other;

    }


4.  这个新类型允许您表示特定的数据点,我们也知道了这些数据点只接受分配时预先定义的值集合。 当然,熟练的程序员可以用静态常量实现这项功能,如下所示:
清单 1 public static final 的常量
   public class OldGrade {  
    
   public static final int A = 1;  
   public static final int B = 2;  
   public static final int C = 3;  
   public static final int D = 4;  
   public static final int F = 5;  
   public static final int INCOMPLETE = 6;  
   } 


然后您就可以让类接受像 OldGrade.B 这样的常量,但是在这样做的时候,请记住这类常量是 Java 中 int 类型的常量,这意味着该方法可以接受任何 int 类型的值,即使它和OldGrade 中定的所有级别都不对应。因此,您需要检测上界和下界,在出现无效值的时候,可能还要包含一个 IllegalArgumentException。而且,如果后来又添加另外一个级别(例如OldGrade.WITHDREW_PASSING),那么必须改变所有代码中的上界,才能接受这个新值。

换句话说,在使用这类带有整型常量的类时,该解决方案也许可行,但并不是非常有效。幸运的是,枚举提供了更好的方法。

定义枚举清单 2 使用了一个可以提供与清单 1 相似的功能的枚举:
    package com.oreilly.tiger.ch03;  
     
    public enum Grade {  
    A, B, C, D, F, INCOMPLETE  
    }; 


在这里,我使用了新的关键字 enum,为 enum 提供了一个名称,并指定了允许的值。然后,Grade 就变成了一个枚举类型,您可以按清单 3 所示的方法使用它:

清单 3. 使用枚举类型
   package com.oreilly.tiger.ch03;  
    
   public class Student {  
    
   private String firstName;  
   private String lastName;  
   private Grade grade;  
    
   public Student(String firstName, String lastName) {  
   this.firstName = firstName;  
   this.lastName = lastName;  
   }  
    
   public void setFirstName(String firstName) {  
   this.firstName = firstName;  
   }  
    
   public String getFirstName() {  
   return firstName;  
   }  
    
   public void setLastName(String lastName) {  
   this.lastName = lastName;  
   }  
    
   public String getLastName() {  
   return lastName;  
   }  
    
   public String getFullName() {  
   return new StringBuffer(firstName)  
   .append(" ")  
   .append(lastName)  
   .toString();  
   }  
    
   public void assignGrade(Grade grade) {  
   this.grade = grade;  
   }  
    
   public Grade getGrade() {  
   return grade;  
   }  
   } 


用以前定义过的类型建立一个新的枚举(grade)之后,您就可以像使用其他成员变量一样使用它了。当然,枚举只能分配枚举值中的一个(例如,A、C 或 INCOMPLETE)。而且,在assignGrade() 中是没有进行错误检测的代码,也没有考虑边界情况,请注意这是如何做到。

在枚举间切换

能够在枚举的值之间移动很好,但是更重要的是根据枚举的值进行决策。您当然可以写一堆if (grade.equals(Grade.A)) 类型的语句,但那是在浪费时间。Tiger 能够很方便地把枚举支持添加到过去的好东西 switch 语句上,所以它很容易使用,而且适合您已知的内容。清单 4向将展示如何解决这个难题:

清单 4. 在枚举之间切换
 public void testSwitchStatement(PrintStream out) throws IOException {  
   StringBuffer outputText = new StringBuffer(student1.getFullName());  
    
   switch (student1.getGrade()) {  
   case A:  
   outputText.append(" excelled with a grade of A");  
   break;  
   case B: // fall through to C  
   case C:  
   outputText.append(" passed with a grade of ")  
   .append(student1.getGrade().toString());  
   break;  
   case D: // fall through to F  
   case F:  
   outputText.append(" failed with a grade of ")  
   .append(student1.getGrade().toString());  
   break;  
   case INCOMPLETE:  
   outputText.append(" did not complete the class.");  
   break;  
   }  
    
   out.println(outputText.toString());  
  }


在这里,枚举值被传递到 switch 语句中(请记住,getGrade() 是作为 Grade 的实例返回的),而每个 case 子句将处理一个特定的值。该值在提供时没有枚举前缀,这意味着不用将代码写成 case Grade.A,只需将其写成 case A 即可。如果您不这么做,编译器不会接受有前缀的值。

现在,您应该已经了解使用 switch 语句时的基本语法,但是还有一些事情您需要知道。在使用 switch 之前进行计划正如您所期待的,在使用枚举和 switch 时,您可以使用 default 语句。清单 5 显示了这个用法:
清单 5. 添加一个 default 块
 public void testSwitchStatement(PrintStream out) throws IOException {  
   StringBuffer outputText = new StringBuffer(student1.getFullName());  
    
   switch (student1.getGrade()) {  
   case A:  
   outputText.append(" excelled with a grade of A");  
   break;  
   case B: // fall through to C  
   case C:  
   outputText.append(" passed with a grade of ")  
   .append(student1.getGrade().toString());  
   break;  
   case D: // fall through to F  
   case F:  
   outputText.append(" failed with a grade of ")  
   .append(student1.getGrade().toString());  
   break;  
   case INCOMPLETE:  
   outputText.append(" did not complete the class.");  
   break;  
   default:  
   outputText.append(" has a grade of ")  
   .append(student1.getGrade().toString());  
   break;  
   }  
    
   out.println(outputText.toString());  
  }


研究以上代码可以看出,任何没有被 case 语句处理的枚举值都会被 default 语句处理。这项技术您应当坚持采用。原因是:假设 Grade 枚举被您的小组中其他程序员修改(而且他忘记告诉您这件事)成清单 6 所示的版本:

清单 6. 给 Grade 枚举添加一个值
 package com.oreilly.tiger.ch03;  
   
  public enum Grade {  
  A, B, C, D, F, INCOMPLETE,  
  WITHDREW_PASSING, WITHDREW_FAILING  
  }


现在,如果使用清单 4 的代码所示的新版 Grade,那么这两个新值会被忽略。更糟的是,您甚至看不到错误!在这种情况下,存在某种能够通用的 default 语句是非常重要的。清单 5 无法很好地处理这些值,但是它会提示您还有其他值,您需要处理这些值。一旦完成处理,您就会有一个继续运行的应用程序,而且它不会忽略这些值,甚至还会指导您下一步的动作。所以这是一个良好的编码习惯。

枚举和集合您所熟悉的使用 public static final 方法进行编码的那些东西,可能已经转而采用枚举的值作为映射的键。如果您不知道其中的含义,请参见清单 7,它是一个公共错误信息的示例,在使用 Ant 的 build 文件时,可能会弹出这样的消息,如下所示:

清单 7. Ant 状态码
   package com.oreilly.tiger.ch03;  
    
   public enum AntStatus {  
   INITIALIZING,  
   COMPILING,  
   COPYING,  
   JARRING,  
   ZIPPING,  
   DONE,  
   ERROR  
   } 


为每个状态码分配一些人们能读懂的错误信息,从而允许人们在 Ant 提供某个代码时查找合适的错误信息,将这些信息显示在控制台上。这是映射(Map)的一个绝好用例,在这里,每个映射(Map)的键都是一个枚举值,而每个值都是键的错误信息。清单 8 演示了该映射的工作方式:

清单 8. 枚举的映射(Map)

   public void testEnumMap(PrintStream out) throws IOException {  
   // Create a map with the key and a String message  
   EnumMap antMessages =  
   new EnumMap(AntStatus.class);  
    
   // Initialize the map  
   antMessages.put(AntStatus.INITIALIZING, "Initializing Ant...");  
   antMessages.put(AntStatus.COMPILING, "Compiling Java classes...");  
   antMessages.put(AntStatus.COPYING, "Copying files...");  
   antMessages.put(AntStatus.JARRING, "JARring up files...");  
   antMessages.put(AntStatus.ZIPPING, "ZIPping up files...");  
   antMessages.put(AntStatus.DONE, "Build complete.");  
   antMessages.put(AntStatus.ERROR, "Error occurred.");  
    
   // Iterate and print messages  
   for (AntStatus status : AntStatus.values() ) {  
   out.println("For status " + status + ", message is: " +  
   antMessages.get(status));  
   }  
   } 


该代码使用了泛型(generics)和新的 EnumMap 构造来建立新映射。而且,枚举值是通过其 Class 对象提供的,同时提供的还有映射值的类型(在该例中,它只是一个简单的字符串)。该方法的输出如清单 9 所示:

Java枚举类型中的 Class 对象您可能已经注意到,清单 8 中的示例代码实际上表明 Tiger 把枚举当作类,这可以从AntStatus 的 Class 对象那里得到证明,该对象不仅可用,而且正被实际使用。这是真的。归根到底, Tiger 还是把枚举看成是特殊的类类型。
清单 9. 清单 8 的输出
  [echo] Running AntStatusTester...  
  [java] For status INITIALIZING, message is: Initializing Ant...  
  [java] For status COMPILING, message is: Compiling Java classes...  
  [java] For status COPYING, message is: Copying files...  
  [java] For status JARRING, message is: JARring up files...  
  [java] For status ZIPPING, message is: ZIPping up files...  
  [java] For status DONE, message is: Build complete.  
  [java] For status ERROR, message is: Error occurred. 


更进一步枚举也可以与集合结合使用,而且非常像新的 EnumMap 构造,Tiger 提供了一套新的EnumSet实现,允许您使用位操作符。另外,可以为枚举添加方法,用它们实现接口,定义叫作特定值的类的实体,在该实体中,特定的代码被附加到枚举的具体值上。这些特性超出了本文的范围,但是在其他地方,有详细介绍它们的文档。

5. 使用Java枚举类型,但是不要滥用

学习任何新版语言的一个危险就是疯狂使用新的语法结构。如果这样做,那么您的代码就会突然之间有 80% 是泛型、标注和枚举。所以,应当只在适合使用枚举的地方才使用它。那么,枚举在什么地方适用呢?一条普遍规则是,任何使用常量的地方,例如目前用 switch代码切换常量的地方。如果只有单独一个值(例如,鞋的最大尺寸,或者笼子中能装猴子的最大数目),则还是把这个任务留给常量吧。但是,如果定义了一组值,而这些值中的任何一个都可以用于特定的数据类型,那么将枚举用在这个地方最适合不过。































分享到:
评论

相关推荐

    Java枚举类型Enum的用法

    Java枚举类型(Enum)是Java SE 5.0引入的一种新的数据类型,它为开发者提供了更为强大且安全的方式来表示一组常量。枚举在Java中不仅是一个类,还是一种特殊的类型,允许我们定义自己的常量集合。接下来,我们将...

    理解java枚举类型

    Java枚举类型是Java语言中的一种特殊的类,用于定义一组固定的常量集合。它在Java中扮演着重要的角色,提供了一种安全、类型化的常量表示方式,比传统的final static变量更具有优势。理解Java枚举类型对于编写高质量...

    Java枚举类型.pdf

    总结起来,Java枚举类型是一个强大的工具,它可以替代`static final`常量来表示一组固定的值,提供类型安全、易于维护的代码。通过枚举,我们可以更方便地管理常量,减少错误,并增加代码的灵活性。枚举是Java中一个...

    简单总结java枚举类型

    Java枚举类型是Java语言中一种特殊的类,用于定义固定的常量集合,它提供了一种安全、类型化的方式来表示一组有限的值。枚举在Java中被引入为一个完整的类型,不同于C++中的枚举,这使得Java枚举更加强大且易于使用...

    java枚举类型说明

    ### Java枚举类型详解 #### 一、引言 在Java编程语言中,枚举(Enum)作为一种重要的数据类型,在程序设计中扮演着不可或缺的角色。本文将深入探讨Java枚举类型的特性和用法,并通过具体实例说明其优势所在。枚举...

    JAVA枚举类型的用法详解

    下面我们将深入探讨Java枚举类型的常见用法。 首先,枚举类型在本质上等同于定义了一组静态常量。创建枚举类型的语法如下: ```java public enum Belle { para1, para2, para3, para4; } ``` 这会创建一个名为`...

    全面掌握java枚举类型(enum-type)1

    全面掌握 Java 枚举类型(enum type) Java 枚举类型是一种特殊的数据类型,它可以取有限个数的明确值。枚举类型的引入解决了 int 枚举模式和 String 枚举模式的缺点,提供了类型安全性、可读性和性能等优势。 ...

    如何使用Java枚举类型

    一个关于JAVA枚举类型的MHT文件,包含JAVA枚举类型的用法,以及JDK版本的问题。

    Java 枚举类型案例.zip

    在本压缩包中,"Java 枚举类型案例"可能包含了多个示例,以帮助我们深入理解Java枚举类型的使用。 1. **枚举的定义** 在Java中,枚举是通过关键字`enum`来定义的。例如,创建一个表示星期的枚举类型: ```java ...

    java枚举类型enum的使用

    ### Java 枚举类型 enum 的使用详解 #### 一、枚举类型概述 在 Java 中,枚举(enum)是一种特殊的类,它被用来表示一组固定的常量值。枚举类型自 JDK 1.5 开始引入,为开发者提供了一种更加安全、简洁的方式来...

    2021年JAVA枚举类型知识点.docx

    以下是关于Java枚举类型的详细知识点: 1. **枚举定义**: - 枚举类型通过`enum`关键字来定义,例如`public enum Color {RED, BLUE, BLACK, YELLOW, GREEN}`。 - 枚举实际上是`java.lang.Enum`类的子类,这意味着...

    JAVA枚举类型[归纳].pdf

    JAVA枚举类型[归纳].pdf

    高阶Java-Java枚举类型 enum 应用详解

    本篇文章将深入探讨Java枚举的特性、用法以及如何在实际开发中充分利用它们。 一、枚举的基本用法 1. 声明枚举类型: ```java public enum Color { RED, GREEN, BLUE } ``` 在这里,`Color`是枚举类型,`RED`, `...

    java枚举结果类、根据状态值获取枚举值

    java枚举结果类、根据状态值获取枚举值 Controller: /** 模块类型枚举 */ model.addAttribute("mType", ModuleTypeEnum.ModuleTypeShow()); ftl: value="${mType.key}:${mType.value}” &lt;/#list&gt;

    java枚举类型优秀源代码

    - **自动实现接口**:枚举类型会自动实现`java.lang.Enum&lt;E&gt;`接口,其中`E`是枚举类型的名称。 - **安全性**:枚举类型的成员是固定的,不允许外部添加或修改成员。 - **单例模式**:每个枚举成员都是单例的,只能被...

    JAVA枚举类型的用法.doc

    - 虽然Java枚举不能直接继承其他枚举或类,但它们隐式地扩展了`java.lang.Enum`类,因此可以实现接口。 7. **枚举与switch语句**: - 枚举常量可以用于`switch`语句,这是枚举类型的一个重要应用场景: ```java ...

    Java枚举类型在switch语句正确使用方法详解

    Java 枚举类型在 switch 语句正确使用方法详解 Java 枚举类型是 Java 语言中的一种特殊类型,用于定义一组具有一些公共特征的常量。Java 枚举类型在 switch 语句中的正确使用方法是本文的主要内容。通过示例代码,...

    Java枚举类型enum的详解及使用

    Java 枚举类型 enum 的详解及使用 Java 枚举类型 enum 是 Java 语言中的一种特殊的数据类型,自从 JDK 1.5 引入枚举类型以来,它已经成为了 Java 程序员中的一种常用工具。本文将对 Java 枚举类型 enum 进行详解,...

Global site tag (gtag.js) - Google Analytics