今天我浏览了这个网站上的一些问题,我发现一个枚举被用于单例模式,据称线程安全的好处,这种解决方案。
我从来没用过枚举,而且我已经用Java编程好几年了。显然,他们改变了很多。现在他们甚至在自己内部完全支持OOP。
为什么我应该在日常编程中使用enum ?
今天我浏览了这个网站上的一些问题,我发现一个枚举被用于单例模式,据称线程安全的好处,这种解决方案。
我从来没用过枚举,而且我已经用Java编程好几年了。显然,他们改变了很多。现在他们甚至在自己内部完全支持OOP。
为什么我应该在日常编程中使用enum ?
当前回答
为什么要使用编程语言的特性?我们有语言的原因是
程序员以计算机可以使用的形式有效而正确地表达算法。 维护人员理解他人编写的算法并正确地进行更改。
枚举提高了正确性和可读性的可能性,而无需编写大量的样板文件。如果你愿意写样板文件,那么你可以“模拟”枚举:
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语句
上面的静态最终枚举模拟没有给你的一件事是良好的开关情况。对于枚举类型,Java开关使用其变量的类型来推断枚举情况的范围,因此对于上面的枚举Color,你只需要说:
Color color = ... ;
switch (color) {
case RED:
...
break;
}
注意它不是颜色。箱子里有红色。如果你不使用enum,使用switch的命名量的唯一方法是:
public Class Color {
public static final int RED = 0;
public static final int AMBER = 1;
public static final int GREEN = 2;
}
但是现在保存颜色的变量必须是int类型。漂亮的编译器检查枚举和静态最终模拟消失了。不快乐。
折衷的方法是在模拟中使用标量值成员:
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);
}
Now:
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
}
which gives us the same thing. We can get away with this because Java enums are implemented as full classes with only a little syntactic sugar sprinkled over the top. This is again less boilerplate, but it's non-obvious unless the idiom is familiar to you. I also dislike the fact that you get the various enum functions even though they don't make much sense for the singleton: ord and values, etc. (There's actually a trickier simulation where Color extends Integer that will work with switch, but it's so tricky that it even more clearly shows why enum is a better idea.)
线程安全
只有在没有锁定的情况下惰性地创建单例时,线程安全才会成为潜在的问题。
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仍然为空,则可以创建任意数量的实例。这很糟糕。唯一的解决方案是添加同步访问来保护变量INSTANCE。
然而,上面的静态最终代码没有这个问题。它在类加载时急切地创建实例。类加载是同步的。
enum单例实际上是惰性的,因为它直到第一次使用才初始化。Java初始化也是同步的,因此多个线程不能初始化instance的多个实例。你得到了一个惰性初始化的单例,只有很少的代码。唯一的缺点是语法相当模糊。您需要了解习惯用法或彻底理解类加载和初始化是如何工作的,才能了解发生了什么。
其他回答
枚举就像类。与类一样,它也有方法和属性。
不同阶级的区别是: 1. 枚举常量是public, static, final。 2. 枚举不能用于创建对象,也不能扩展其他类。但是它可以实现接口。
使用枚举类型安全,这是一个语言特性,所以你通常会得到:
编译器支持(立即查看类型问题) ide中的工具支持(在切换情况下自动补全,缺失情况,强制默认,…) 在某些情况下,枚举的性能也很好(EnumSet,类型安全的替代传统的基于int的“位标志”)。
枚举可以有方法、构造函数,甚至可以在枚举中使用枚举,并将枚举与接口结合使用。
可以将枚举看作是替换一组定义良好的int常量的类型(Java从C/ c++“继承”了int常量),在某些情况下还可以替换位标志。
《Effective Java 2nd Edition》一书中有一整章是关于它们的,并且有更多的细节。也可以参考Stack Overflow的文章。
Something none of the other answers have covered that make enums particularly powerful are the ability to have template methods. Methods can be part of the base enum and overridden by each type. And, with the behavior attached to the enum, it often eliminates the need for if-else constructs or switch statements as this blog post demonstrates - where enum.method() does what originally would be executed inside the conditional. The same example also shows the use of static imports with enums as well producing much cleaner DSL like code.
其他一些有趣的特性包括枚举提供equals()、toString()和hashCode()的实现,并实现Serializable和Comparable。
我强烈推荐Bruce Eckel的《Thinking in Java》第4版,它用了整整一章来讨论这个主题。特别具有启发性的例子是将石头剪子布(即RoShamBo)游戏作为枚举。
在我的经验中,我看到Enum的使用有时会导致系统很难改变。如果您正在为一组频繁更改的特定于域的值使用Enum,并且它有许多依赖于它的其他类和组件,那么您可能要考虑不使用Enum。
例如,一个交易系统使用Enum进行市场/交易所。市场有很多,几乎可以肯定的是,会有很多子系统需要进入这个市场列表。每当你想要一个新的市场添加到你的系统中,或者如果你想要删除一个市场,有可能太阳下的一切都将不得不重建和释放。
A better example would be something like a product category type. Let's say your software manages inventory for a department store. There are a lot of product categories, and many reasons why this list of categories could change. Managers may want to stock a new product line, get rid of other product lines, and possibly reorganize the categories from time to time. If you have to rebuild and redeploy all of your systems simply because users want to add a product category, then you've taken something that should be simple and fast (adding a category) and made it very difficult and slow.
总之,如果您所表示的数据随着时间的推移是非常静态的,并且有有限数量的依赖项,则枚举是很好的选择。但是如果数据变化很大并且有很多依赖关系,那么就需要一些在编译时不检查的动态的东西(比如数据库表)。
Enum表示枚举,即逐个提到(一些事情)。
enum是一种包含固定常数集的数据类型。
OR
枚举就像一个类,在编译时有一组固定的实例。
例如:
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());
}
}
}
枚举的优点:
Enum提高了编译时检查的类型安全性,以避免在运行时出现错误。 枚举可以很容易地用于交换机 枚举可以遍历 枚举可以有字段、构造函数和方法 enum可以实现许多接口,但不能扩展任何类,因为它在内部扩展了enum类
更多的