Java枚举与具有公共静态最终字段的类相比有什么优势?

时间:2012-04-02 00:45:37

标签: java enums

我对C#非常熟悉,但开始在Java中工作更多。我希望得知Java中的枚举基本上与C#中的枚举相同,但显然情况并非如此。最初,我很高兴得知Java枚举可以包含多个数据,这看起来非常有利(http://docs.oracle.com/javase/tutorial/java/javaOO/enum.html)。然而,从那时起我发现C#中缺少很多功能,例如能够轻松地为枚举元素分配特定值,从而能够在没有相当大努力的情况下将整数转换为枚举(即Convert integer value to matching Java Enum)。

所以我的问题是:对于带有一堆公共静态最终字段的类,Java枚举是否有任何好处?或者它只提供更紧凑的语法?

编辑:让我更清楚一点。与具有相同类型的相同的公共静态最终字段的类相比,Java枚举有什么好处?例如,在第一个链接的行星示例中,枚举对具有这些公共常量的类有什么好处:

public static final Planet MERCURY = new Planet(3.303e+23, 2.4397e6);
public static final Planet VENUS = new Planet(4.869e+24, 6.0518e6);
public static final Planet EARTH = new Planet(5.976e+24, 6.37814e6);
public static final Planet MARS = new Planet(6.421e+23, 3.3972e6);
public static final Planet JUPITER = new Planet(1.9e+27, 7.1492e7);
public static final Planet SATURN = new Planet(5.688e+26, 6.0268e7);
public static final Planet URANUS = new Planet(8.686e+25, 2.5559e7);
public static final Planet NEPTUNE = new Planet(1.024e+26, 2.4746e7);

据我所知,卡萨布兰卡的答案是唯一能满足这一要求的答案。

17 个答案:

答案 0 :(得分:91)

  1. 类型安全和价值安全。
  2. 保证单身。
  3. 能够定义和覆盖方法。
  4. 无需资格即可在switch语句case语句中使用值。
  5. 通过ordinal().
  6. 内置值序列化
  7. 按名称而不是按值进行序列化,这提供了一定程度的面向未来。
  8. EnumSetEnumMap个类。

答案 1 :(得分:73)

没有人提到在switch陈述中使用它们的能力;我也会把它扔进去。

这允许以干净的方式使用任意复杂的枚举,而不使用instanceof,可能会混淆if序列或非字符串/ int切换值。规范示例是状态机。

答案 2 :(得分:70)

从技术上讲,人们确实可以将枚举视为一个带有一堆类型常量的类,这实际上是如何在内部实现枚举常量。但是,使用enum会为您提供有用的方法(Enum javadoc),否则您必须自己实施这些方法,例如Enum.valueOf

答案 3 :(得分:42)

混淆较少。以Font为例。它有一个构造函数,它带有你想要的Font的名称,它的大小和样式(new Font(String, int, int))。直到今天,我还记不起风格或尺寸是否先行。如果Font使用enum表示其所有不同的样式(PLAINBOLDITALICBOLD_ITALIC),则其构造函数会显示像Font(String, Style, int)一样,防止任何混淆。不幸的是,enum类在创建Font类时并不存在,并且由于Java必须保持反向兼容性,我们总是会受到这种模糊性的困扰。

当然,这只是使用enum而不是public static final常量的论据。枚举也适用于singletons并实现默认行为,同时允许以后的自定义(I.E. strategy pattern)。后者的一个示例是java.nio.file的{​​{3}}和OpenOption:如果开发人员想要创建自己的非标准OpenOption,他就可以。

答案 4 :(得分:42)

主要优点是类型安全。使用一组常量,可以使用相同内在类型的任何值,从而引入错误。使用枚举时,只能使用适用的值。

例如

public static final int SIZE_SMALL  = 1;
public static final int SIZE_MEDIUM = 2;
public static final int SIZE_LARGE  = 3;

public void setSize(int newSize) { ... }

obj.setSize(15); // Compiles but likely to fail later

VS

public enum Size { SMALL, MEDIUM, LARGE };

public void setSize(Size s) { ... }

obj.setSize( ? ); // Can't even express the above example with an enum

答案 5 :(得分:24)

这里有很多好的答案,但没有一个提到有专门针对枚举的Collection API类/接口高度优化的实现

这些枚举特定类只接受Enum个实例(EnumMap仅接受Enum仅作为键),并且只要有可能,它们会在其实现中恢复为紧凑表示和位操作

这是什么意思?

如果我们的Enum类型不超过64个元素(大多数现实Enum示例将符合此条件),则实现将元素存储在单个long值中,有问题的每个Enum实例将与此64位长long的位相关联。将元素添加到EnumSet只是将正确的位设置为1,删除它只是将该位设置为0.测试元素是否在Set中只是一个位掩码测试!现在你必须为此爱Enum

答案 6 :(得分:11)

正如您已经注意到的,枚举的第一个好处是语法简洁。但是枚举的主要目的是提供一组众所周知的常量,默认情况下,这些常量形成一个范围,并通过类型和方式帮助执行更全面的代码分析。价值安全检查。

枚举的这些属性可以帮助程序员和编译器。例如,假设您看到一个接受整数的函数。整数意味着什么?你能传递什么样的价值观?你真的不知道。但是如果你看到一个接受枚举的函数,你就会非常清楚所有可以传入的值。

对于编译器,枚举有助于确定一系列值,除非您为枚举成员指定特殊值,否则它们的范围从0到更高。这有助于通过类型安全检查等自动跟踪代码中的错误。例如,编译器可能会警告您在switch语句中没有处理所有可能的枚举值(即,当您没有default大小写并且只处理N个枚举值中的一个时)。当你将任意整数转换为枚举时,它也会发出警告,因为枚举的值范围小于整数,而这反过来可能会触发函数中不会真正接受整数的错误。此外,当值从0开始时,为开关生成跳转表变得更容易。

这不仅适用于Java,也适用于其他具有严格类型检查的语言。 C,C ++,D,C#就是很好的例子。

答案 7 :(得分:10)

示例:

public class CurrencyDenom {
   public static final int PENNY = 1;
 public static final int NICKLE = 5;
 public static final int DIME = 10;
public static final int QUARTER = 25;}

java常量的限制

1)无类型安全:首先,它不是类型安全的;你可以将任何有效的int值赋给int,例如99虽然没有硬币来代表这个价值。

2)无意义打印:任何这些常量的打印值将打印其数值而不是有意义的硬币名称,例如当您打印NICKLE时,它将打印" 5"而不是" NICKLE"

3)无名称空间:要访问currencyDenom常量,我们需要为类名添加前缀,例如CurrencyDenom.PENNY而不仅仅是使用PENNY,虽然这也可以通过在JDK 1.5中使用静态导入来实现

枚举的优点

1)Java中的枚举是类型安全的,并且有自己的名称空间。这意味着你的枚举将有一个类型,例如"货币"在下面的示例中,您不能分配除枚举常量中指定的任何值。

public enum Currency {PENNY, NICKLE, DIME, QUARTER};

Currency coin = Currency.PENNY; coin = 1; //compilation error

2)Java中的Enum是类或接口的引用类型,您可以在java Enum中定义构造函数,方法和变量,这使得它在C和C ++中比Enum更强大,如下面的Java Enum类型示例所示。

3)您可以在创建时指定枚举常量的值,如下例所示: public enum Currency {PENNY(1),NICKLE(5),DIME(10),QUARTER(25)}; 但为了实现这一点,你需要定义一个成员变量和一个构造函数,因为PENNY(1)实际上是在调用一个接受int值的构造函数,见下面的例子。

public enum Currency {
    PENNY(1), NICKLE(5), DIME(10), QUARTER(25);
    private int value;

    private Currency(int value) {
            this.value = value;
    }
}; 

参考:https://javarevisited.blogspot.com/2011/08/enum-in-java-example-tutorial.html

答案 8 :(得分:4)

枚举好处:

  1. 枚举是类型安全的,静态字段不是
  2. 存在有限数量的值(无法传递不存在的枚举值。如果您有静态类字段,则可以犯错误)
  3. 每个枚举可以分配多个属性(字段/ getter) - 封装。还有一些简单的方法:YEAR.toSeconds()或类似方法。比较:Colors.RED.getHex()与Colors.toHex(Colors.RED)
  4. “例如能够轻松地将枚举元素分配给某个值”

    enum EnumX{
      VAL_1(1),
      VAL_200(200);
      public final int certainValue;
      private X(int certainValue){this.certainValue = certainValue;}
    }
    

    “因此能够在没有相当大努力的情况下将整数转换为枚举” 添加一个将int转换为枚举的方法。只需添加包含映射的静态HashMap。

    如果你真的想将ord = VAL_200.ordinal()转换回val_200,只需使用:EnumX.values()[ord]

答案 9 :(得分:4)

枚举是隐含最终的,具有私有构造函数,其所有值都属于同一类型或子类型,您可以使用values()获取其所有值,获取其name()或{ {1}}值或您可以按编号或名称查找枚举。

你也可以定义子类(即使在概念上是最终的,你也不能做任何其他方式)

ordinal()

答案 10 :(得分:3)

另一个重要区别是java编译器将基本类型 String static final字段视为文字。这意味着这些常量变为内联。它与C/C++ #define预处理器类似。见this SO question。枚举不是这种情况。

答案 11 :(得分:2)

使用枚举时,您将获得有效值的编译时检查。看this question.

答案 12 :(得分:2)

最大的优点是enum Singletons易于编写和线程安全:

public enum EasySingleton{
    INSTANCE;
}

/**
* Singleton pattern example with Double checked Locking
*/
public class DoubleCheckedLockingSingleton{
     private volatile DoubleCheckedLockingSingleton INSTANCE;

     private DoubleCheckedLockingSingleton(){}

     public DoubleCheckedLockingSingleton getInstance(){
         if(INSTANCE == null){
            synchronized(DoubleCheckedLockingSingleton.class){
                //double checking Singleton instance
                if(INSTANCE == null){
                    INSTANCE = new DoubleCheckedLockingSingleton();
                }
            }
         }
         return INSTANCE;
     }
}

两者都相似,它通过实现

自行处理序列化
//readResolve to prevent another instance of Singleton
    private Object readResolve(){
        return INSTANCE;
    }

more

答案 13 :(得分:0)

枚举可以是本地的

Java 16 开始,枚举可以在本地(在方法内)定义。除了能够将枚举定义为嵌套或单独的类之外,此范围还可以。

这个新的本地定义范围与新的记录功能一起出现。有关详细信息,请参阅 JEP 395: Records。枚举、接口和记录都可以在 Java 16+ 中本地定义。

相比之下,public static final 字段始终具有全局范围。

答案 14 :(得分:0)

主要原因:枚举可帮助您编写结构良好的代码,其中参数的语义含义在编译时清晰明了且具有强类型-出于其他答案的所有原因。

十分严格的:在Java中,开箱即用的是Enum成员数组。通常这很好,因为它可以帮助评估安全性和测试价值,但是在某些情况下,这可能是一个缺点,例如,如果您要从库中扩展现有的基本代码。相反,如果在具有静态字段的类中使用相同的数据,则可以在运行时轻松地添加该类的新实例(您可能还需要编写代码以将其添加到该类的任何Iterable中)。但是Enums的这种行为可以更改:使用反射,您可以在运行时添加新成员或替换现有成员,尽管这可能仅应在没有其他选择的特殊情况下才能完成:例如,这是一个棘手的解决方案,可能会产生意外问题,在Can I add and remove elements of enumeration at runtime in Java上查看我的答案。

答案 15 :(得分:0)

此处列出的枚举有很多优点,我现在正在按照问题的要求创建此类枚举。 但是我有一个5-6个字段的枚举。

Example: <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
<property name="prefix" value="/WEB-INF/view1/"/>
<property name="prefix" value="/WEB-INF/view2/"/>
<property name="suffix" value=".jsp"/>
</bean>

在这种情况下,当枚举中有多个字段时,很难理解哪个值属于哪个字段,因为您需要查看构造函数和注意事项。

具有enum Planet{ EARTH(1000000, 312312321,31232131, "some text", "", 12), .... other planets .... 常量的类,并使用static final模式创建此类对象,使其更具可读性。但是,如果需要,您将失去使用枚举的所有其他优点。 这种类的一个缺点是,您需要手动将Builder对象添加到Planet的{​​{1}}

与此类相比,我仍然更喜欢枚举,因为list/set派上了用场,并且您永远都不知道将来是否需要在Planets.values()switch中使用它们:)

答案 16 :(得分:0)

我认为enum不能是final,因为在引擎盖编译器下为每个enum条目生成子类。

更多信息来自source