什么是枚举?它们为什么有用?

今天我在这个网站上浏览了一些问题,我发现有一个enum 用于单例模式提到了这种解决方案的线程安全好处。

我从来没用过enums,而且我已经用Java编程好几年了。显然,他们改变了很多。现在他们甚至在自己内部完全支持OOP。

现在为什么我应该在日常编程中使用enum ?< / h3 >

371026 次浏览

从Java 文档 -

你应该在任何时候使用enum类型 需要表示一个固定的集合 常量。包括自然enum 比如我们太阳系中的行星 你知道的系统和数据集 编译时所有可能的值 时间——例如,a上的选项 菜单,命令行标志,等等

一个常见的例子是用枚举类型替换一个类,用一组私有静态final int常量(常量数量合理)。基本上,如果你认为你在编译时知道“something”的所有可能值,你可以将其表示为枚举类型。比起带有常量的类,枚举提供了可读性和灵活性。

枚举类型几乎没有其他优点。它们总是特定枚举类的一个实例(因此出现了使用枚举作为单例的概念)。另一个优点是可以在switch-case语句中使用枚举作为类型。此外,您还可以在枚举上使用toString ()将它们打印为可读字符串。

当一个变量(特别是一个方法参数)只能从一组可能的值中选取一个时,您应该始终使用枚举。例如类型常量(合约状态:“永久”,“临时”,“学徒”)或标志(“立即执行”,“延迟执行”)。

如果使用枚举而不是整数(或字符串代码),则增加了编译时检查,避免了传入无效常量的错误,并且记录了哪些值是可以合法使用的。

顺便说一句,过度使用枚举可能意味着你的方法做了太多的事情(通常有几个单独的方法会更好,而不是一个方法有几个标记来修改它所做的事情),但如果你必须使用标记或类型代码,枚举是最好的选择。

举个例子,哪个更好?

/** Counts number of foobangs.
* @param type Type of foobangs to count. Can be 1=green foobangs,
* 2=wrinkled foobangs, 3=sweet foobangs, 0=all types.
* @return number of foobangs of type
*/
public int countFoobangs(int type)

/** Types of foobangs. */
public enum FB_TYPE {
GREEN, WRINKLED, SWEET,
/** special type for all types combined */
ALL;
}


/** Counts number of foobangs.
* @param type Type of foobangs to count
* @return number of foobangs of type
*/
public int countFoobangs(FB_TYPE type)

方法调用如下:

int sweetFoobangCount = countFoobangs(3);

然后就变成:

int sweetFoobangCount = countFoobangs(FB_TYPE.SWEET);

在第二个例子中,可以立即清楚地看到哪些类型是允许的,文档和实现不能不同步,编译器可以强制执行这一点。 还有一个无效的调用,如

int sweetFoobangCount = countFoobangs(99);

已经不可能了。

现在为什么我应该用什么 在日常编程中枚举?< / p >

您可以使用Enum来表示较小的固定常数集或内部类模式,同时增加可读性。此外,在方法参数中使用enum时,可以加强一定的刚性。它们提供了一种有趣的可能性,可以将信息传递给甲骨文网站上的行星例子这样的构造函数,而且正如您所发现的那样,还允许以一种简单的方式创建单例模式。

例如:Locale.setDefault(Locale.US)读起来比Locale.setDefault(1)更好,并且当您添加.分隔符而不是所有整数时,强制使用IDE中显示的固定值集。

其他答案都没有提到的使枚举特别强大的东西是拥有模板方法的能力。方法可以是基本枚举的一部分,并被每种类型覆盖。而且,通过将行为附加到枚举,它通常消除了对if-else结构或switch语句的需求,如博客文章演示 -其中enum.method()执行最初将在条件中执行的内容。同一个示例还展示了使用枚举的静态导入,以及生成更清晰的类似DSL的代码。

其他一些有趣的特性包括枚举为equals()toString()hashCode()提供实现,并实现SerializableComparable

我强烈推荐Bruce Eckel的用Java第4版思考,这本书用了整整一章的篇幅来讨论枚举所能提供的内容。特别具有启发性的例子是将石头剪子布(即RoShamBo)游戏作为枚举。

ENum代表“枚举类型”。它是一种具有一组固定常数的数据类型,由您自己定义。

枚举是什么

  • enum是为枚举定义的关键字,是一种新的数据类型。应该广泛使用类型安全枚举。特别是,它们是较老api中用来表示相关项集的简单String或int常量的健壮替代。

为什么使用enum

  • enum是java.lang.Enum的隐式final子类
  • 如果枚举是类的成员,则它是隐式静态的
  • New永远不能用于枚举,即使是在枚举类型本身中
  • name和valueOf简单地使用枚举常量的文本,而toString可以被覆盖以提供任何内容,如果需要的话
  • 对于枚举常量,equals和==等于同一个东西,并且可以互换使用
  • 枚举常量是隐式的公共静态final

请注意

  • 枚举不能扩展任何类。
  • enum不能是超类。
  • 枚举常量的出现顺序被称为它们的“自然顺序”,并定义了其他项使用的顺序:compareTo,值的迭代顺序,EnumSet, EnumSet.range。
  • 枚举可以有构造函数、静态和实例块、变量和方法,但不能有抽象方法。

不管别人怎么说…在我曾经工作过的一个旧项目中,实体(独立应用程序)之间的大量通信使用代表一个小集合的整数。使用静态方法将set声明为enum,以便从value获取enum对象,反之亦然。代码看起来更干净,开关案例可用性和更容易写入日志。

enum ProtocolType {
TCP_IP (1, "Transmission Control Protocol"),
IP (2, "Internet Protocol"),
UDP (3, "User Datagram Protocol");


public int code;
public String name;


private ProtocolType(int code, String name) {
this.code = code;
this.name = name;
}


public static ProtocolType fromInt(int code) {
switch(code) {
case 1:
return TCP_IP;
case 2:
return IP;
case 3:
return UDP;
}


// we had some exception handling for this
// as the contract for these was between 2 independent applications
// liable to change between versions (mostly adding new stuff)
// but keeping it simple here.
return null;
}
}
从接收到的值(例如1,2)使用ProtocolType.fromInt(2)创建enum对象 使用myEnumObj.name

写入日志

希望这能有所帮助。

在我看来,你到目前为止的所有答案都是正确的,但根据我的经验,我想用几句话来表达:

# EYZ0

否则,您可以像往常一样使用字符串(可能您为应用程序定义了一些“约定”),并且您将非常灵活……但是你不能在你的字符串上获得100%的安全,你只能在运行时意识到它们。

# eyz0枚举一组固定的值,以自记录的方式 它们使你的代码更显式,也更不容易出错

为什么不使用String,或int,而不是Enum,常量?

  1. 编译器不允许拼写错误,这两个值都不是固定的 set,因为枚举本身是类型。后果:
    • 您不必编写前提条件(或手册if)来确保您的参数在有效范围内。
    • 类型不变是免费的。
    • 李< / ul > < / >
    • 枚举可以有行为,就像任何其他类一样。
    • 无论如何,使用Strings可能需要类似数量的内存(这取决于Enum的复杂性)。

此外,Enum的每个实例都是一个类,您可以为其定义单独的行为。

另外,它们在创建实例时(当加载枚举时)确保线程安全,这在简化单例模式方面有很好的应用。

这个博客说明了它的一些应用程序,例如用于解析器的状态机

除了前面提到的用例,我经常发现枚举对于实现策略模式很有用,遵循一些基本的面向对象原则:

  1. 将代码放在数据所在的位置(即在枚举本身中——或者通常在枚举常量中,这可能会覆盖方法)。
  2. 实现一个(或更多)接口,以便不将客户端代码绑定到枚举(它应该只提供一组默认实现)。

最简单的例子是一组Comparator实现:

enum StringComparator implements Comparator<String> {
NATURAL {
@Override
public int compare(String s1, String s2) {
return s1.compareTo(s2);
}
},
REVERSE {
@Override
public int compare(String s1, String s2) {
return NATURAL.compare(s2, s1);
}
},
LENGTH {
@Override
public int compare(String s1, String s2) {
return new Integer(s1.length()).compareTo(s2.length());
}
};
}

这种“模式”可以在更复杂的场景中使用,广泛使用枚举附带的所有优点:遍历实例,依赖于它们的隐式顺序,根据实例名称检索实例,为特定上下文提供正确实例的静态方法等等。你仍然把这些都隐藏在接口后面,这样你的代码就可以在不需要修改的情况下使用自定义实现,以防你想要一些“默认选项”中不可用的东西。

我曾看到这种方法成功地应用于时间粒度(每天、每周等)概念的建模,其中所有逻辑都封装在枚举中(为给定的时间范围选择正确的粒度,将特定行为绑定到每个粒度作为常量方法等)。而且,在服务层看来,Granularity只是一个接口。

到目前为止,我从未需要使用枚举。自从它们在1.5或tiger版本中被引入以来,我就一直在阅读有关它们的文章。他们从未真正为我解决过一个“问题”。对于那些使用它的人(我看到很多人这样做),我确信它绝对符合一些的目的。只有我的2英镑。

为什么要使用编程语言的特性?我们有语言的原因是

  1. 程序员以计算机可以使用的形式高效地表达算法。
  2. 维护人员理解其他人编写的算法,并对正确进行更改。

枚举提高了正确性和可读性的可能性,而无需编写大量的样板文件。如果你愿意写样板文件,那么你可以“模拟”枚举:

public class Color {
private Color() {} // Prevent others from making colors.
public static final Color RED = new Color();
public static final Color AMBER = new Color();
public static final Color GREEN = new Color();
}

现在你可以这样写:

Color trafficLightColor = Color.RED;

上面的样板具有与

public enum Color { RED, AMBER, GREEN };

两者都提供来自编译器的相同级别的检查帮助。样板文件只是更多的输入。但是节省大量的输入使程序员更加非常高效。(见1),所以这是一个值得的特性。

至少还有一个原因是值得的:

Switch语句

上面的static final枚举模拟给你的是漂亮的switch案例。对于枚举类型,Java开关使用其变量的类型来推断枚举情况的范围,因此对于上面的enum Color,你只需要说:

Color color = ... ;
switch (color) {
case RED:
...
break;
}

注意在这些情况下不是Color.RED。如果你不使用enum,使用switch命名量的唯一方法是这样的:

public Class Color {
public static final int RED = 0;
public static final int AMBER = 1;
public static final int GREEN = 2;
}

但是现在保存颜色的变量类型必须是int。枚举的编译器检查和static final模拟消失了。不快乐。

折衷的方法是在模拟中使用标量值成员:

public class Color {
public static final int RED_TAG = 1;
public static final int AMBER_TAG = 2;
public static final int GREEN_TAG = 3;


public final int tag;


private Color(int tag) { this.tag = tag; }
public static final Color RED = new Color(RED_TAG);
public static final Color AMBER = new Color(AMBER_TAG);
public static final Color GREEN = new Color(GREEN_TAG);
}

现在:

Color color = ... ;
switch (color.tag) {
case Color.RED_TAG:
...
break;
}

但请注意,更多的是样板文件!

使用枚举作为单例

从上面的样板,你可以看到为什么枚举提供了一种实现单例的方法。而不是写:

public class SingletonClass {
public static final void INSTANCE = new SingletonClass();
private SingletonClass() {}


// all the methods and instance data for the class here
}

然后访问它

SingletonClass.INSTANCE

我们可以说

public enum SingletonClass {
INSTANCE;


// all the methods and instance data for the class here
}

结果是一样的。我们可以这样做,因为Java枚举实现为完整的类,只是在上面撒了一点语法糖。这也不是一个模式化的例子,但它并不明显,除非您熟悉这个习语。我也不喜欢你得到各种枚举函数,即使它们对单例没有多大意义:ordvalues等(实际上有一个更棘手的模拟,Color extends Integer将与switch一起工作,但它是如此棘手,它甚至更清楚地表明为什么enum是一个更好的主意)。

线程安全

只有在没有锁定的情况下惰性地创建单例时,线程安全才会成为潜在的问题。

public class SingletonClass {
private static SingletonClass INSTANCE;
private SingletonClass() {}
public SingletonClass getInstance() {
if (INSTANCE == null) INSTANCE = new SingletonClass();
return INSTANCE;
}


// all the methods and instance data for the class here
}

如果许多线程同时调用getInstance,而INSTANCE仍然为空,则可以创建任意数量的实例。这很糟糕。唯一的解决方案是添加synchronized访问来保护变量INSTANCE

然而,上面的static final代码没有这个问题。它在类加载时急切地创建实例。类加载是同步的。

enum单例实际上是懒惰的,因为它直到第一次使用才初始化。Java初始化也是同步的,因此多个线程不能初始化INSTANCE的多个实例。你得到了一个惰性初始化的单例,只有很少的代码。唯一的缺点是语法相当模糊。您需要了解习惯用法或彻底理解类加载和初始化是如何工作的,才能了解发生了什么。

Enum继承了Object类和抽象类Enum的所有方法。所以你可以使用它的方法来反射、多线程、序列化、可比性等等。如果你只是声明一个静态常量而不是Enum,你就不能。除此之外,Enum的值也可以传递给DAO层。

下面是要演示的示例程序。

public enum State {


Start("1"),
Wait("1"),
Notify("2"),
NotifyAll("3"),
Run("4"),
SystemInatilize("5"),
VendorInatilize("6"),
test,
FrameworkInatilize("7");


public static State getState(String value) {
return State.Wait;
}


private String value;
State test;


private State(String value) {
this.value = value;
}


private State() {
}


public String getValue() {
return value;
}


public void setCurrentState(State currentState) {
test = currentState;
}


public boolean isNotify() {
return this.equals(Notify);
}
}


public class EnumTest {


State test;


public void setCurrentState(State currentState) {
test = currentState;
}


public State getCurrentState() {
return test;
}


public static void main(String[] args) {
System.out.println(State.test);
System.out.println(State.FrameworkInatilize);
EnumTest test=new EnumTest();
test.setCurrentState(State.Notify);
test. stateSwitch();
}


public void stateSwitch() {
switch (getCurrentState()) {
case Notify:
System.out.println("Notify");
System.out.println(test.isNotify());
break;
default:
break;
}
}
}

枚举?为什么要使用它?我认为当你使用它的时候会更容易理解。我也有同样的经历。

假设您有一个创建、删除、编辑和读取数据库操作。

现在如果你创建一个枚举作为一个操作:

public enum operation {
create("1")
delete("2")
edit("3")
read("4")


// You may have is methods here
public boolean isCreate() {
return this.equals(create);
}
// More methods like the above can be written


}

现在,你可以这样声明:

private operation currentOperation;


// And assign the value for it
currentOperation = operation.create

所以你可以在很多方面使用它。对于特定的事情使用enum总是好的,因为上面示例中的数据库操作可以通过检查currentOperation来控制。也许有人会说,这也可以用变量和整数值来实现。但我相信Enum更安全,更适合程序员。

另一件事:我认为每个程序员都喜欢布尔,不是吗?因为它只能存储两个值,两个特定的值。所以Enum可以被认为具有相同类型的功能,用户可以定义它将存储多少和什么类型的值,只是方式略有不同。:)

这里有很多答案,只想指出两个具体的:

1)在Switch-case语句中使用常量。 Switch case不允许使用String对象作为case。枚举可以派上用场。: # EYZ0 < / p >

2)再次实现Singleton Design Pattern - Enum,来拯救。用法,这里:在Java中使用Enum作为单例的最佳方法是什么?

使用枚举类型安全,这是一个语言特性,所以你通常会得到:

  • 编译器支持(立即查看类型问题)
  • ide中的工具支持(在切换情况下自动补全,缺失情况,强制默认,…)
  • 在某些情况下,枚举的性能也很棒(,类型安全替代传统的基于int的“位标志”。)

枚举可以有方法、构造函数,甚至可以在枚举中使用枚举,并将枚举与接口结合使用。

可以将枚举看作是替换一组定义良好的int常量的类型(Java从C/ c++“继承”了int常量),在某些情况下还可以替换位标志。

Effective Java第二版这本书有一整章都是关于它们的,并且有更多的细节。参见这篇Stack Overflow文章

enum的意思是枚举eration,即逐一提到(许多事情)。

枚举是一个包含固定常数集的数据类型。

# EYZ2

例如:

public class EnumExample {
interface SeasonInt {
String seasonDuration();
}


private enum Season implements SeasonInt {
// except the enum constants remaining code looks same as class
// enum constants are implicitly public static final we have used all caps to specify them like Constants in Java
WINTER(88, "DEC - FEB"), SPRING(92, "MAR - JUN"), SUMMER(91, "JUN - AUG"), FALL(90, "SEP - NOV");


private int days;
private String months;


Season(int days, String months) { // note: constructor is by default private
this.days = days;
this.months = months;
}


@Override
public String seasonDuration() {
return this+" -> "+this.days + "days,   " + this.months+" months";
}


}
public static void main(String[] args) {
System.out.println(Season.SPRING.seasonDuration());
for (Season season : Season.values()){
System.out.println(season.seasonDuration());
}


}
}

# EYZ0

  • Enum提高了编译时检查的类型安全性,以避免在运行时出现错误。
  • 枚举可以很容易地用于交换机
  • 枚举可以遍历
  • 枚举可以有字段、构造函数和方法
  • enum可以实现许多接口,但不能扩展任何类,因为它在内部扩展了enum类

为# EYZ0

Java允许您将变量限制为只有几个预定义值之一——换句话说,枚举列表中的一个值。 使用enums可以帮助减少代码中的bug。 下面是一个在类外使用enums的例子
enums coffeesize{BIG , HUGE , OVERWHELMING };
//This semicolon is optional.

这限制了coffeesize的变量:BIGHUGEOVERWHELMING

知道enums就像其他有Constant字段和private constructor字段的类一样是有用的。

例如,

public enum Weekday
{
MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY
}

编译器按如下方式编译它;

class Weekday extends Enum
{
public static final Weekday MONDAY  = new Weekday( "MONDAY",   0 );
public static final Weekday TUESDAY = new Weekday( "TUESDAY ", 1 );
public static final Weekday WEDNESDAY= new Weekday( "WEDNESDAY", 2 );
public static final Weekday THURSDAY= new Weekday( "THURSDAY", 3 );
public static final Weekday FRIDAY= new Weekday( "FRIDAY", 4 );
public static final Weekday SATURDAY= new Weekday( "SATURDAY", 5 );
public static final Weekday SUNDAY= new Weekday( "SUNDAY", 6 );


private Weekday( String s, int i )
{
super( s, i );
}


// other methods...
}

让我恍然大悟的是:Enum有一个私有构造函数,只能通过公共枚举来访问:

enum RGB {
RED("Red"), GREEN("Green"), BLUE("Blue");


public static final String PREFIX = "color ";


public String getRGBString() {
return PREFIX + color;
}


String color;


RGB(String color) {
this.color = color;
}
}


public class HelloWorld {
public static void main(String[] args) {
String c = RGB.RED.getRGBString();
System.out.print("Hello " + c);
}
}

对于我来说,使代码在未来可读的枚举最有用的适用情况是在下一个代码片段中表示:

public enum Items {
MESSAGES, CHATS, CITY_ONLINE, FRIENDS, PROFILE, SETTINGS, PEOPLE_SEARCH, CREATE_CHAT
}


@Override
public boolean onCreateOptionsMenu(Menu menuPrm) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menuPrm);
View itemChooserLcl;
for (int i = 0; i < menuPrm.size(); i++) {
MenuItem itemLcl  = menuPrm.getItem(i);
itemChooserLcl = itemLcl.getActionView();
if (itemChooserLcl != null) {
//here Im marking each View' tag by enume values:
itemChooserLcl.setTag(Items.values()[i]);
itemChooserLcl.setOnClickListener(drawerMenuListener);
}
}
return true;
}
private View.OnClickListener drawerMenuListener=new View.OnClickListener() {
@Override
public void onClick(View v) {
Items tagLcl= (Items) v.getTag();
switch (tagLcl){
case MESSAGES: ;
break;
case CHATS : ;
break;
case CITY_ONLINE : ;
break;
case FRIENDS : ;
break;
case  PROFILE: ;
break;
case  SETTINGS: ;
break;
case  PEOPLE_SEARCH: ;
break;
case  CREATE_CHAT: ;
break;
}
}
};

在我的经验中,我看到Enum的使用有时会导致系统很难改变。如果您正在为一组频繁更改的特定于域的值使用Enum,并且它有许多依赖于它的其他类和组件,那么您可能需要考虑使用Enum来使用

例如,一个交易系统使用Enum进行市场/交易所。市场有很多,几乎可以肯定的是,会有很多子系统需要进入这个市场列表。每当你想要一个新的市场添加到你的系统中,或者如果你想要删除一个市场,有可能太阳下的一切都将不得不重建和释放。

一个更好的例子是产品类别类型。假设您的软件管理一家百货商店的库存。有很多产品类别,而且这个类别列表可能会改变的原因也很多。经理们可能想要储备一个新的产品线,摆脱其他产品线,并可能不时地重组类别。如果仅仅因为用户想要添加一个产品类别,您就必须重新构建和重新部署所有的系统,那么您已经采取了一些本应简单和快速的事情(添加一个类别),并使其变得非常困难和缓慢。

总之,如果您所表示的数据随着时间的推移是非常静态的,并且有有限数量的依赖项,则枚举是很好的选择。但是如果数据变化很大并且有很多依赖关系,那么就需要一些在编译时不检查的动态的东西(比如数据库表)。

我会使用枚举作为一个有用的映射工具,避免多个if-else

public enum Mapping {


ONE("1"),
TWO("2");


private String label;


private Mapping(String label){
this.label = label;
}


public static Mapping by(String label) {


for(Mapping m: values() {
if(m.label.equals(label)) return m;
}


return null;
}


}

因此,方法by(String label)允许您通过非枚举获取枚举值。此外,还可以创建两个枚举之间的映射。还可以尝试“1对多”或“多对多”,除了“一对一”的默认关系

最后,enum是一个Java类。因此,您可以在其中包含main方法,当需要立即在args上执行一些映射操作时,这可能很有用。

而不是做一堆const int声明

您可以将它们都分组在一个enum中

所以它们都是由它们所属的共同群体组织起来的

枚举就像类。与类一样,它也有方法和属性。

与类的区别是: 1. 枚举常量是public, static, final。 2. 枚举不能用于创建对象,也不能扩展其他类。但是它可以实现接口

基于enum的单例

一个老问题的现代视角

这种方法通过利用Java的保证来实现单例,即任何enum值在Java程序中只实例化一次,并且enum为线程安全提供了隐式支持。由于Java枚举值是全局可访问的,因此它们可以作为单例使用。

public enum Singleton {
SINGLETON;
public void method() { }
}

这是如何工作的呢?好吧,代码的第二行可以被认为是这样的:

public final static Singleton SINGLETON = new Singleton();

我们得到了早期初始化的单例。

记住,因为这是一个枚举,你总是可以通过Singleton. SINGLETON访问实例:

Singleton s = Singleton.SINGLETON;

优势

  • 为了防止在反序列化期间创建其他单例实例,请使用基于枚举的单例,因为枚举的序列化由JVM负责。枚举序列化和反序列化的工作方式与普通java对象不同。唯一被序列化的是枚举值的名称。在反序列化过程中,枚举valueOf方法与反序列化的名称一起使用,以获得所需的实例。
  • 基于枚举的单例可以保护自身不受反射攻击。enum类型实际上扩展了java Enum类。反射不能用于实例化枚举类型的对象的原因是java规范不允许,并且该规则被编码在Constructor类的newInstance方法的实现中,该方法通常用于通过反射创建对象:
if ((clazz.getModifiers() & Modifier.ENUM) != 0)
throw new IllegalArgumentException("Cannot reflectively create enum objects");
  • Enum不应该被克隆,因为每个值必须只有一个实例。
  • 所有单例实现中最简洁的代码。

缺点

  • 基于枚举的单例不允许延迟初始化。
  • 如果你改变了你的设计,想要将你的单例转换为多例,enum不允许这样做。多吨模式用于受控地创建多个实例,它通过使用map来管理这些实例。多吨模式不是使用单个实例每个应用程序(例如java.lang.Runtime),而是使用单个实例每个键
  • Enum仅在Java 5中出现,因此不能在以前的版本中使用它。

单例模式有几种实现方式,每一种都有优缺点。

  • 急装单件
  • 双重检查锁定单例
  • 初始化-按需holder习语
  • 基于enum的单例

详细的描述每个都太啰嗦了,所以我只放了一个链接到一篇好文章——所有你想知道的关于Singleton

除了@BradB,回答:

这是真的……奇怪的是,这是唯一一个提到这一点的答案。当初学者发现枚举时,他们很快就会将其视为检查编译器的有效标识符的魔术。当代码打算用于分布式系统时,他们会哭……几个月后。保持与包含非静态值列表的枚举的向后兼容性是一个真正值得关注和痛苦的问题。这是因为当您向现有枚举添加值时,它的类型会改变(尽管名称不会改变)。

“哦,等等,它可能看起来是同一种类型,对吧?毕竟,它们是具有相同名称的枚举——而且枚举在本质上不就是整数吗?”由于这些原因,编译器可能不会标记使用类型本身的一个定义,而它期望使用另一个定义。但事实上,它们(在最重要的方面)是不同类型的。最重要的是,它们具有不同的数据域——给定类型可接受的值。通过添加一个值,我们有效地改变了枚举的类型,从而打破了向后兼容性。

总之:当你想使用它时,请使用检查所使用的数据域是一个有限的、已知的、固定的集合