今天我正在浏览这个网站上的一些问题,我发现提到了一个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)
与
/** 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);
不再可能.
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();
}
现在你可以写:
Color trafficLightColor = Color.RED;
上面的样板与效果大致相同
public enum Color { RED, AMBER, GREEN };
两者都提供了与编译器相同级别的检查帮助.Boilerplate只是更多的打字.但是节省大量的输入会使程序员更有效率(见1),所以这是一个有价值的功能.
至少还有一个原因是值得的:
切换语句
static final上面的枚举模拟没有给你的一件事是很好的switch情况.对于枚举类型,Java开关使用其变量的类型来推断枚举案例的范围,因此对于enum Color上述内容,您只需要说:
Color color = ... ;
switch (color) {
    case RED:
        ...
        break;
}
注意它不是Color.RED在这种情况下.如果您不使用枚举,则使用命名数量的唯一方法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枚举是作为完整的类实现的,只有少量的语法糖洒在顶层.这又是一个很少的样板,但除非你熟悉成语,否则它是不明显的.我也喜欢你得到的各种枚举功能,即使他们没有为单身多大意义的事实: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
}
如果许多线程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());
        }
    };
}
这种"模式"可用于更复杂的场景,广泛使用枚举附带的所有好东西:迭代实例,依赖于它们的隐式顺序,通过名称检索实例,提供正确实例的静态方法对于特定的上下文等等,你仍然将这一切都隐藏在界面背后,这样你的代码就可以使用自定义实现而无需修改,以防你想要"默认选项"中没有的东西.
我已经看到这成功应用于建模时间粒度(每日,每周等)的概念,其中所有逻辑都封装在枚举中(为给定时间范围选择正确的粒度,将每个粒度绑定的特定行为作为常量方法等).而且,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
} 
编译器将其编译如下;
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...
}
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());
        }
    }
}
枚举的优点:
对于更多
除了其他人所说的..在我过去工作的旧项目中,实体(独立应用程序)之间的大量通信使用了代表一小组的整数.将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;
    }
}
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;
        }
    }
}
将枚举用于 TYPE SAFETY,这是一种语言功能,因此您通常会得到:
枚举可以有方法、构造函数,你甚至可以在枚举中使用枚举并将枚举与接口结合起来。
将枚举视为替换一组明确定义的 int 常量(Java 从 C/C++“继承”)的类型,并在某些情况下替换位标志。
《Effective Java 2nd Edition》一书有一整章关于它们,并有更详细的介绍。另请参阅此堆栈溢出帖子。
| 归档时间: | 
 | 
| 查看次数: | 278032 次 | 
| 最近记录: |