Java理解Enums

 2019-04-29 

Understanding Enums in Java

什么是Java枚举?它们是如何工作的?我可以在哪里使用它们?如何使用?我可以不在应用程序中使用枚举吗?或者它们的功能强大到使用它们比忽略它们更好吗?


Java 5中的Enums基本上是具有预定义的实例集的类。它们是用来替换整数常量集合的。它们最好是常量,因为它们可以加强类型安全性。

因此,而不是:

1
2
3
4
5
6
public class Suit {
  public final static int SPADES = 1;
  public final static int CLUBS = 2
  public final static int HEARTS = 3;
  public final static int DIAMONDS = 4;
}

你有:

1
2
3
public enum Suit {
  SPADES, CLUBS, HEARTS, DIAMONDS
}

其优点是:

  • 类型安全。可以将函数参数、返回类型、类成员或局部变量声明为特定的枚举类型,编译器将强制类型安全;
  • 枚举基本上是类。它们可以实现接口,具有行为等等。
  • 类型安全性是一个问题,因为在第一个示例中,这些是有效的语句:

    1
    int i = Suit.DIAMONDS * Suit.CLUBS;

    或者,您可以将11传递给一个需要西装的函数。不能使用类型安全枚举执行此操作。

    您可以使用一个类来提供类型安全性,这是Java 5之前的解决方案。Josh Bloch(在JavaJava中,它是Java程序员IMRO必须阅读的),它促进了成为Java 5 +枚举的Type SeaFe枚举模式。它上面有大量的样板文件,还有一些人们不喜欢处理的角落案例,例如序列化而不是调用构造函数,为了确保只有一个实例,您必须重写readresolve()方法。

    例如:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    public enum CardColour {
      RED, BLACK
    }

    public enum Suit {
      SPADES(CardColour.BLACK),
      CLUBS(CardColour.BLACK),
      HEARTS(CardColour.RED),
      DIAMONDS(CardColour.RED);

      private final CardColour colour;

      Suit(CardColour colour) { this.colour = colour; }

      public CardColour getColour() { return colour; }
    }

    编辑:Sun介绍了类型安全枚举。

    至于接口,它们实际上是对枚举的补充,而不是作为可选的。就像你可以说西服是一个界面,你可以这样说:

    1
    2
    3
    public interface Suit {
      CardColour getColour();
    }

    问题是你可以去定义300种不同的西装,你也可以定义几次黑桃。枚举的另一个优点是(尽管有类装入角的情况),每个枚举值只有一个实例。通常,这被称为具有规范值,这意味着该等同性为真:

    1
    a.equals(b) == b.equals(a) == (a == b)

    对于所有是特定枚举实例的A、B。这意味着,与其写:

    1
    if (card.getSuit().equals(Suit.SPADES)) { ... }

    你可以写:

    1
    if (card.getSuit() == Suit.SPADES) { ... }

    它更快,而且通常更容易阅读。另外,如果您比较不同类型的枚举说它们不可能相等,IDES通常会给您反馈,这是一种有用的早期错误检查形式。


    把枚举想象成如下

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    public class MyEnum {

        // Object instantiated at declaration
        public static final MyEnum ONE = new MyEnum();
        public static final MyEnum TWO = new MyEnum();
        public static final MyEnum THREE = new MyEnum();

        // Notice a private constructor
        // There is no way outside MyEnum class call it
        private MyEnum() { ... }


    }

    所以作为枚举的myenum

    1
    2
    3
    4
    5
    public enum MyEnum {
        ONE,
        TWO,
        THREE;
    }

    两者都相似

    当做,


    为了添加一些额外的信息来进一步了解它,最好从变量及其值的概念开始。现在我确信,每个变量都有一个声明类型(例如Java基元类型)。每种类型都有自己的对应值。例如,char类型的变量可以接受所有单个字符作为其值。因此,当您想要声明一个变量可以接受所有字符常量集时,这是非常有用的。例如,所有字符常量都是可接受的,并且对于一个cartostopoprogram变量是有意义的:

    1
    char aCharToStopProgram = getUserInput();

    但是,当你有一个有限的值时,我的意思是变量的值域被限制为特殊的值。例如,您有保留系统多项选择问题值的用户选择。所以"a"、"b"、"c"、"d"才有意义。您不希望在程序代码的其他部分(或任何其他原因)为该变量分配无意义的值,如"s"。现在,对我们的变量使用char原语类型是正确的,它可以接受任何字符常量,比如下面的。

    1
    char userChoice = getUserChoice();

    在这种情况下,Java提供枚举类型。这意味着,通过枚举关键字,您可以定义一个新类,该类具有有限和固定的实例,这些实例称为命名值(不能创建新对象)。例如:

    1
    enum MultipleChoiceAnswer { A, B, C, D };

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    public enum MultipleChoiceAnswer {

        A('a'), B('b'), C('c'), D('d');

        private char choice;

        private MultipleChoiceAnswer(char choice) {
            this.choice = choice;
        }

        public String getChoice() {
            return choice;
        }
    }

    因此,您可以使用自己的类型定义变量:

    1
    MultipleChoiceAnswer userChoice = getUserChoice();//OR MultipleChoiceAnswer.A

    如果情况不好,你就不需要他们了。

    它们允许你有一套定义明确的事物,例如,如果你想代表物质的状态,你可以:

    1
    2
    3
    4
    5
    enum MatterState {
        Solid,
        Liquid,
        Gas;
    }

    它们以多种方式击败了使用对象表示集的旧风格,其中一些方式是:

    • 你可以在开关中使用它们声明。
    • 更少的代码。
    • 内置的到/来自字符串。

    Sun的枚举文档可能是最好的解释。当然,在Java 1.5发布之前,Java程序员当然可以不用它们。通常在1.5之前使用Java版本中的常量来完成同样的事情。但枚举是一种很好的方便。


    从我的解释来看,枚举比其他任何东西都更易于阅读。它们基本上用于用更多描述性的名称替换1-6这样的值,例如[高兴、悲伤、愤怒等]当需要使用一组小变量来描述您期望的解决方案时,应该使用它们。