枚举:为什么?什么时候? [英] Enumerations: Why? When?

查看:119
本文介绍了枚举:为什么?什么时候?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我是一名几乎毕业的计算机科学学生,在我的编码生涯中,我发现很少的例子,除了代表标准卡片面孔的典型案例之外,枚举也被使用。 p>

你知道枚举在日常编码中使用的任何聪明的方式吗?



为什么枚举这样重要的是,在什么情况下应该能够识别构建枚举是最好的方法?

解决方案

枚举 EnumMap EnumSet



枚举的情况



从Java 6开始, java.util.Calendar 是一个凌乱的例子,可以从使用枚举(以及其他改进)中获益很多。



目前日历定义了以下常量(等等):

  // int constant antipattern from java.util.Calendar 
public static final int JANUARY = 0;
public static final int FEBRUARY = 1;
...
public static final int SUNDAY = 1;
public static final int MONDAY = 2;
...

这些都是 int ,即使它们显然代表不同的概念实体。



以下是一些严重后果:




  • 很脆弱;在需要的时候,必须注意分配不同的数字。


    • 如果错误地设置 MONDAY = 0; SUNDAY = 0 ; ,那么我们有 MONDAY == SUNDAY


  • 没有命名空间,没有类型安全,因为一切都只是一个 int


    • 我们可以 setMonth(JANUARY),但我们也可以 setMonth(THURSDAY) setMonth(42)

    • 谁知道什么 (一个真正的方法!)!




    • 相比之下,我们可以这样做:

        //日历库的假设枚举
      枚举月{
      JANUARY,FEBRUARY,...
      }
      枚举DayOfWeek {
      SUNDAY,MONDAY,...
      }

      现在我们不必担心大约 MONDAY == SUNDAY (永远不会发生!),而且由于 Month DayOfWeek 是不同的类型, setMonth(MONDAY)不编译。



      此外,在这里是一些前后代码:

        // BEFORE with int constants 
      for(int month = JANUARY;月<= DECEMBER;月++){
      ...
      }

      这里我们正在做所有各种假设,例如 JANUARY + 1 == FEBRUARY 等。另一方面,枚举对应更简洁,更可读,并且做出较少的假设(因此更少的错误机会):

        // AFTER with enum 
      for(Month月:Month.values()){
      ...
      }






      实例字段



      在Java中,枚举是一个 class 具有许多特殊属性,但是,如果需要,可以定义实例方法和字段。 / p>

      请考虑以下示例:

        // BEFORE:with int constants 
      public static final int NORTH = 0;
      public static final int EAST = 1;
      public static final int SOUTH = 2;
      public static final int WEST = 3;

      public static int degreeFor(int direction){
      return direction * 90; //相当假设!
      //必须与int常量保持同步!


      // ...
      for(int dir = NORTH; dir< = WEST; dir ++){
      ... degreeFor(dir)。 ..
      }

      另一方面,使用枚举你可以这样写:

       枚举方向{
      NORTH(0),EAST (90),南(180),西(270);
      //这么明显!所以很容易阅读!所以很容易写!所以很容易维护!

      private final int degree;
      Direction(int degree){this.degree = degree; }
      public int getDegree(){return degrees; }
      }

      // ...
      (方向dir:Direction.values()){
      ... dir.getDegree()...
      }



      实例方法的情况



      考虑以下示例:

        static int apply(int op1,int op2,int operator){
      switch(operator){
      case PLUS:return op1 + op2;
      case MINUS:return op1 - op2;
      case ...
      default:throw new IllegalArgumentException(Unknown operator!);
      }
      }

      如上例所示, enum 在Java中可以有实例方法,但不仅如此,但每个常量也可以有自己的具体 @Override 。这将显示在以下代码中:

       枚举运算符{
      PLUS {int apply(int op1,int op2) {return op1 + op2; }},
      MINUS {int apply(int op1,int op2){return op1 - op2; }},
      ...
      ;
      abstract int apply(int op1,int op2);
      }






      EnumMap



      以下是有效的Java第2版的引用:


      不得从其枚举相关联的值 .com / j2se / 1.5.0 / docs / api / java / lang / Enum.html#ordinal%28%29rel =nofollow noreferrer> ordinal() ;将其存储在实例字段中。 ( 31:使用实例字段而不是ordinals )使用ordinals来索引数组是非常合适的:使用 EnumMap 而不是。一般原则是应用程序员很少使用 Enum.ordinal 。 (<项目33:使用枚举地图而不是序号索引)


      基本上与以前一样,您可能会有这样的情况:

        // BEFORE,使用int常量和数组索引
      员工[] employeeOfTheMonth = ...

      employeeOfTheMonth [JANUARY] = jamesBond;

      现在您可以:

        // AFTER,with enum and EnumMap 
      Map< Month,Employee> employeeOfTheMonth = ...

      employeeOfTheMonth.put(Month.JANUARY,jamesBond);






      EnumSet



      通常使用两个 int 常量的幂在C ++中表示位集。这依赖于按位操作。一个例子可能是这样的:

        public static final int BUTTON_A = 1; 
      public static final int BUTTON_B = 2;
      public static final int BUTTON_X = 4;
      public static final int BUTTON_Y = 8;

      int buttonState = BUTTON_A | BUTTON_X; // A& X被按下!

      if((buttonState& BUTTON_B)!= 0){// B被按下...
      ...
      }

      使用枚举 EnumSet 这可以看起来像这样:

       枚举按钮{
      A,B,X,Y;
      }

      设置< Button> buttonState = EnumSet.of(Button.A,Button.X); // A& X被按下!

      if(buttonState.contains(Button.B)){// B被按下...
      ...
      }



      参考文献





      另请参见




      • 有效的Java 2nd $ 30
        $ b

        • 项目30:使用枚举而不是 int项目31:使用实例字段而不是ordinals

        • 项目32:使用 EnumSet 项目33:使用 EnumMap 而不是顺序索引




        • 相关问题




          的主要候选人

          I am an almost-graduating computer science student, and throughout my coding career, I've found very few instances where enumerations, except for canonical cases such as representing the faces of a standard deck of cards, are used.

          Do you know of any clever ways that enums are used in everyday coding?

          Why are enumerations so important and in what situations should one be able to identify that building an enumeration is the best approach?

          解决方案

          These are the main arguments for enum, EnumMap, and EnumSet by short examples.

          The case for enum

          As of Java 6, java.util.Calendar is an example of a messy class that could've benefited a lot from using enum (among other improvements).

          Currently Calendar defines the following constants (among many others):

          // int constant antipattern from java.util.Calendar
          public static final int JANUARY = 0;
          public static final int FEBRUARY = 1;
          ...
          public static final int SUNDAY = 1;
          public static final int MONDAY = 2;
          ...
          

          These are all int, even though they obviously represent different conceptual entities.

          The following are some serious consequences:

          • It's brittle; care must be taken to assign different numbers whenever needed.
            • If by mistake we set MONDAY = 0;, SUNDAY = 0;, then we have MONDAY == SUNDAY
          • There is no namespace and no type-safety, since everything is just an int:
            • We can setMonth(JANUARY), but we can also setMonth(THURSDAY) or setMonth(42)
            • Who knows what set(int,int,int,int,int,int) (a real method!) does!

          By contrast, we could have something like this instead:

          // Hypothetical enums for a Calendar library
          enum Month {
             JANUARY, FEBRUARY, ...
          }
          enum DayOfWeek {
             SUNDAY, MONDAY, ...
          }
          

          Now we never have to worry about MONDAY == SUNDAY (it can never happen!), and since Month and DayOfWeek are different types, setMonth(MONDAY) does not compile.

          Additionally, here are some before-and-after codes:

          // BEFORE with int constants
          for (int month = JANUARY; month <= DECEMBER; month++) {
             ...
          }
          

          Here we're making all sorts of assumptions, e.g. JANUARY + 1 == FEBRUARY, etc. On the other hand, the enum counterpart is both more concise, more readable, and makes less assumptions (and therefore less chance for bugs):

          // AFTER with enum
          for (Month month : Month.values()) {
             ...
          }
          


          The case for instance fields

          In Java, enum is a class that has many special properties, but a class nonetheless, allowing you to define instance methods and fields if necessary.

          Consider the following example:

          // BEFORE: with int constants
          public static final int NORTH = 0;
          public static final int EAST  = 1;
          public static final int SOUTH = 2;
          public static final int WEST  = 3;
          
          public static int degreeFor(int direction) {
             return direction * 90; // quite an assumption!
             // must be kept in-sync with the int constants!
          }
          
          //...
          for (int dir = NORTH; dir <= WEST; dir++) {
             ... degreeFor(dir) ...
          }
          

          On the other hand, with enum you can write something like this:

          enum Direction {
             NORTH(0), EAST(90), SOUTH(180), WEST(270);
             // so obvious! so easy to read! so easy to write! so easy to maintain!
          
             private final int degree;
             Direction(int degree)      { this.degree = degree; }
             public int getDegree()     { return degree; }
          }
          
          //...
          for (Direction dir : Direction.values()) {
             ... dir.getDegree() ...
          }
          

          The case for instance methods

          Consider the following example:

          static int apply(int op1, int op2, int operator) {
             switch (operator) {
                case PLUS  : return op1 + op2;
                case MINUS : return op1 - op2;
                case ...
                default: throw new IllegalArgumentException("Unknown operator!");
             }
          }
          

          As shown in previous example, enum in Java can have instance methods, but not only that but each constant can have its own specific @Override as well. This is shown in the following code:

          enum Operator {
              PLUS  { int apply(int op1, int op2) { return op1 + op2; } },
              MINUS { int apply(int op1, int op2) { return op1 - op2; } },
              ...
              ;
              abstract int apply(int op1, int op2);
          }
          


          The case for EnumMap

          Here's a quote from Effective Java 2nd Edition:

          Never derive a value associated with an enum from its ordinal(); store it in an instance field instead. (Item 31: Use instance fields instead of ordinals) It is rarely appropriate to use ordinals to index arrays: use EnumMap instead. The general principle is that application programmers should rarely, if ever, use Enum.ordinal. (Item 33: Use EnumMap instead of ordinal indexing)

          Essentially where as before you may have something like this:

          // BEFORE, with int constants and array indexing
          Employee[] employeeOfTheMonth = ...
          
          employeeOfTheMonth[JANUARY] = jamesBond;
          

          Now you can have:

          // AFTER, with enum and EnumMap
          Map<Month, Employee> employeeOfTheMonth = ...
          
          employeeOfTheMonth.put(Month.JANUARY, jamesBond);
          


          The case for EnumSet

          Power of two int constants are often used e.g. in C++ to denote bit sets. This relies on bitwise operations. An example may look something like this:

          public static final int BUTTON_A = 1;
          public static final int BUTTON_B = 2;
          public static final int BUTTON_X = 4;
          public static final int BUTTON_Y = 8;
          
          int buttonState = BUTTON_A | BUTTON_X; // A & X are pressed!
          
          if ((buttonState & BUTTON_B) != 0) {   // B is pressed...
             ...
          }
          

          With enum and EnumSet, this can look something like this:

          enum Button {
            A, B, X, Y;
          }
          
          Set<Button> buttonState = EnumSet.of(Button.A, Button.X); // A & X are pressed!
          
          if (buttonState.contains(Button.B)) { // B is pressed...
             ...
          }
          

          References

          See also

          • Effective Java 2nd Edition
            • Item 30: Use enum instead of int constants
            • Item 31: Use instance fields instead of ordinals
            • Item 32: Use EnumSet instead of bit fields
            • Item 33: Use EnumMap instead of ordinal indexing

          Related questions

          这篇关于枚举:为什么?什么时候?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆