今天我正在浏览这个网站上的一些问题,我发现提到了一个enum 单独使用模式的关于这种解决方案所谓的线程安全的好处.
我从来没有使用过enums,而且我已经用Java编程了好几年了.显然他们改变了很多.现在他们甚至在自己内部全力支持OOP.
sle*_*ske 606
当变量(尤其是方法参数)只能从一小组可能的值中取出一个时,应始终使用枚举.例如类型常量(合同状态:"永久","临时","学徒")或标志("立即执行","延迟执行").
如果使用枚举而不是整数(或字符串代码),则增加编译时检查并避免传入无效常量时出错,并记录哪些值合法使用.
顺便说一句,过度使用枚举可能意味着你的方法做得太多了(通常有几个单独的方法更好,而不是一个方法需要几个标志来修改它的作用),但如果你必须使用标志或类型代码,枚举是要走的路.
举个例子,哪个更好?
/** 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)
Run Code Online (Sandbox Code Playgroud)
与
/** 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)
Run Code Online (Sandbox Code Playgroud)
方法调用如:
int sweetFoobangCount = countFoobangs(3);
Run Code Online (Sandbox Code Playgroud)
然后变成:
int sweetFoobangCount = countFoobangs(FB_TYPE.SWEET);
Run Code Online (Sandbox Code Playgroud)
在第二个示例中,立即清楚允许哪些类型,文档和实现不会不同步,并且编译器可以强制执行此操作.另外,无效的通话就像
int sweetFoobangCount = countFoobangs(99);
Run Code Online (Sandbox Code Playgroud)
不再可能.
Gen*_*ene 131
为何使用任何编程语言功能?我们有语言的原因是
Enums提高了正确性和可读性的可能性,而无需编写大量样板文件.如果你愿意编写样板文件,那么你可以"模拟"枚举:
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();
}
Run Code Online (Sandbox Code Playgroud)
现在你可以写:
Color trafficLightColor = Color.RED;
Run Code Online (Sandbox Code Playgroud)
上面的样板与效果大致相同
public enum Color { RED, AMBER, GREEN };
Run Code Online (Sandbox Code Playgroud)
两者都提供了与编译器相同级别的检查帮助.Boilerplate只是更多的打字.但是节省大量的输入会使程序员更有效率(见1),所以这是一个有价值的功能.
至少还有一个原因是值得的:
切换语句
static final上面的枚举模拟没有给你的一件事是很好的switch情况.对于枚举类型,Java开关使用其变量的类型来推断枚举案例的范围,因此对于enum Color上述内容,您只需要说:
Color color = ... ;
switch (color) {
case RED:
...
break;
}
Run Code Online (Sandbox Code Playgroud)
注意它不是Color.RED在这种情况下.如果您不使用枚举,则使用命名数量的唯一方法switch是:
public Class Color {
public static final int RED = 0;
public static final int AMBER = 1;
public static final int GREEN = 2;
}
Run Code Online (Sandbox Code Playgroud)
但现在保持颜色的变量必须具有类型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);
}
Run Code Online (Sandbox Code Playgroud)
现在:
Color color = ... ;
switch (color.tag) {
case Color.RED_TAG:
...
break;
}
Run Code Online (Sandbox Code Playgroud)
但请注意,更多的样板!
使用枚举作为单身人士
从上面的样板文件中,您可以看到为什么枚举提供了实现单例的方法.而不是写:
public class SingletonClass {
public static final void INSTANCE = new SingletonClass();
private SingletonClass() {}
// all the methods and instance data for the class here
}
Run Code Online (Sandbox Code Playgroud)
然后使用
SingletonClass.INSTANCE
Run Code Online (Sandbox Code Playgroud)
我们可以说
public enum SingletonClass {
INSTANCE;
// all the methods and instance data for the class here
}
Run Code Online (Sandbox Code Playgroud)
这给了我们同样的东西.我们可以侥幸成功,因为Java枚举是作为完整的类实现的,只有少量的语法糖洒在顶层.这又是一个很少的样板,但除非你熟悉成语,否则它是不明显的.我也喜欢你得到的各种枚举功能,即使他们没有为单身多大意义的事实:ord和values等.(有实际上是一个棘手的模拟,其中Color extends Integer,将与开关的工作,但它是如此棘手,它甚至更清楚地说明为什么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
}
Run Code Online (Sandbox Code Playgroud)
如果许多线程getInstance同时调用而INSTANCE仍为null,则可以创建任意数量的实例.这是不好的.唯一的解决方案是添加synchronized访问权以保护变量INSTANCE.
但是,static final上面的代码没有这个问题.它在类加载时急切地创建实例.类加载是同步的.
的enum,因为它未初始化,直到第一次使用单是有效的懒人.Java初始化也是同步的,因此多个线程无法初始化多个实例INSTANCE.你得到一个懒惰的初始化单例,代码非常少.唯一不好的是相当模糊的语法.你需要知道这个习惯用法或彻底理解类加载和初始化是如何工作的,以了解发生了什么.
Cos*_*atu 42
除了已经提到的用例之外,我经常发现枚举对于实现策略模式很有用,遵循一些基本的OOP准则:
最简单的例子是一组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());
}
};
}
Run Code Online (Sandbox Code Playgroud)
这种"模式"可用于更复杂的场景,广泛使用枚举附带的所有好东西:迭代实例,依赖于它们的隐式顺序,通过名称检索实例,提供正确实例的静态方法对于特定的上下文等等,你仍然将这一切都隐藏在界面背后,这样你的代码就可以使用自定义实现而无需修改,以防你想要"默认选项"中没有的东西.
我已经看到这成功应用于建模时间粒度(每日,每周等)的概念,其中所有逻辑都封装在枚举中(为给定时间范围选择正确的粒度,将每个粒度绑定的特定行为作为常量方法等).而且,Granularity服务层看到的只是一个界面.
ora*_*ips 32
其他答案都没有涵盖,使枚举特别强大的是能够使用模板方法.方法可以是基本枚举的一部分,并由每种类型覆盖.并且,通过附加到枚举的行为,它通常消除了对if-else构造或switch语句的需要,正如此博客文章所示 - enum.method()最初将在条件内执行的内容.同样的例子还显示了使用带枚举的静态导入以及生成更清晰的DSL代码.
其他一些有趣的特质包括事实枚举提供实施equals(),toString()并hashCode()贯彻Serializable和Comparable.
有关枚举所提供的所有内容的完整概述,我强烈推荐Bruce Eckel的Thinking in Java第4版,其中专门讨论了这一主题.特别有启发性的是涉及Rock,Paper,Scissors(即RoShamBo)游戏作为枚举的示例.
Coo*_*ans 22
来自Java 文档 -
您需要在需要表示一组固定常量时使用枚举类型.这包括自然枚举类型,例如太阳系中的行星和数据集,您可以在编译时知道所有可能的值 - 例如,菜单上的选项,命令行标志等.
一个常见的例子是使用枚举类型替换具有一组私有静态final int常量(在合理的常量数内)的类.基本上,如果您认为在编译时知道"某事"的所有可能值,则可以将其表示为枚举类型.枚举为具有常量的类提供了可读性和灵活性.
我可以想到枚举类型的其他几个优点.它们总是特定枚举类的一个实例(因此使用枚举作为单例的概念到达).另一个优点是您可以在switch-case语句中使用枚举作为类型.您还可以在枚举上使用toString()将它们打印为可读字符串.
afs*_*tos 13
Enum以自我记录的方式枚举一组固定的值.
它们使您的代码更加明确,而且更不容易出错.
为什么不使用String,或int代替Enum常数?
if)以确保您的参数处于有效范围内.String无论如何,你可能需要相似数量的内存来使用s(这取决于它的复杂性Enum).而且,每个Enum实例都是一个类,您可以为其定义其个体行为.
此外,它们在创建实例时(在加载枚举时)确保线程安全,这在简化Singleton模式中有很大的应用.
此博客说明了它的一些应用程序,例如解析器的状态机.
Ad *_*tum 13
知道它enums就像其他带有Constant字段和类的类一样很有用private constructor.
例如,
public enum Weekday
{
MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY
}
Run Code Online (Sandbox Code Playgroud)
编译器将其编译如下;
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...
}
Run Code Online (Sandbox Code Playgroud)
Pre*_*raj 12
enum装置枚举关合作即提(一些事情)一个接一个.
一个枚举是包含组固定常数的数据类型.
要么
An
enum就像aclass,在编译时已知一组固定的实例.
例如:
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());
}
}
}
Run Code Online (Sandbox Code Playgroud)
枚举的优点:
对于更多
除了其他人所说的..在我过去工作的旧项目中,实体(独立应用程序)之间的大量通信使用了代表一小组的整数.将set声明为enum静态方法来获取enum对象value和反之亦然是有用的.代码看起来更干净,切换案例可用性并更容易写入日志.
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;
}
}
Run Code Online (Sandbox Code Playgroud)
enum使用" ProtocolType.fromInt(2)
写入日志" 从接收的值(例如1,2)创建对象myEnumObj.name
希望这可以帮助.
Enum继承了Object类和抽象类的所有方法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;
}
}
}
Run Code Online (Sandbox Code Playgroud)
将枚举用于 TYPE SAFETY,这是一种语言功能,因此您通常会得到:
枚举可以有方法、构造函数,你甚至可以在枚举中使用枚举并将枚举与接口结合起来。
将枚举视为替换一组明确定义的 int 常量(Java 从 C/C++“继承”)的类型,并在某些情况下替换位标志。
《Effective Java 2nd Edition》一书有一整章关于它们,并有更详细的介绍。另请参阅此堆栈溢出帖子。
| 归档时间: |
|
| 查看次数: |
278032 次 |
| 最近记录: |