今天我浏览了这个网站上的一些问题,我发现一个枚举被用于单例模式,据称线程安全的好处,这种解决方案。

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

为什么我应该在日常编程中使用enum ?


当前回答

Java允许将变量限制为只有几个预定义值之一——换句话说,枚举列表中的一个值。 使用枚举可以帮助减少代码中的bug。 下面是一个类外部枚举的例子:

enums coffeesize{BIG , HUGE , OVERWHELMING }; 
//This semicolon is optional.

这限制了咖啡的大小:大的,巨大的,或压倒性的变量。

其他回答

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

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;
        }
    }
};

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

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;
    }
}

使用ProtocolType.fromInt(2)从接收的值(例如1,2)创建enum对象 使用myEnumObj.name写入日志

希望这能有所帮助。

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

如果希望编译器检查标识符值的有效性,请使用枚举。

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

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

程序员以计算机可以使用的形式有效而正确地表达算法。 维护人员理解他人编写的算法并正确地进行更改。

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

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的多个实例。你得到了一个惰性初始化的单例,只有很少的代码。唯一的缺点是语法相当模糊。您需要了解习惯用法或彻底理解类加载和初始化是如何工作的,才能了解发生了什么。

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

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

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

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

《Effective Java 2nd Edition》一书中有一整章是关于它们的,并且有更多的细节。也可以参考Stack Overflow的文章。