与带有公共静态 final 字段的类相比,Javaenum 的优势是什么?

我非常熟悉 C # ,但是开始更多地使用 Java。我希望了解到 Java 中的枚举基本上等价于 C # 中的枚举,但显然情况并非如此。最初,我很兴奋地了解到 Java 枚举可以包含多个数据片段,这似乎非常有利(http://docs.oracle.com/javase/tutorial/java/javaOO/enum.html)。然而,从那以后,我发现 C # 中缺少了很多琐碎的特性,比如能够轻松地为一个枚举元素分配一个特定的值,因此能够在没有大量工作的情况下将一个整数转换成枚举(即 将整数值转换为匹配 JavaEnum)。

所以我的问题是: 对于带有一大堆公共静态 final 字段的类,Java 枚举有什么好处吗?或者它只是提供了更紧凑的语法?

编辑: 让我说得更清楚些。Java 枚举相对于一个包含大量公共静态 final 字段 属于同一类型的类有什么好处?例如,在第一个链接的行星示例中,枚举相对于具有这些公共常量的类的优势是什么:

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

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

153135 次浏览

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

比如说

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

public enum Size { SMALL, MEDIUM, LARGE };


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


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

在使用枚举时,您会得到有效值的编译时检查

这样就不会那么混乱了。以 Font2为例。它有一个构造函数,该构造函数采用所需的 Font名称、大小和样式(new Font(String, int, int))。直到今天,我都不记得是风格还是尺寸优先。如果 Font在所有不同的样式(PLAINBOLDITALICBOLD_ITALIC)中都使用了 enum,那么它的构造函数看起来就会像 Font(String, Style, int),避免了任何混淆。不幸的是,当 Font类被创建的时候,enum还没有出现,而且由于 Java 必须保持反向兼容性,我们总是会被这种模糊性所困扰。

当然,这只是使用 enum而不是 public static final常量的一个参数。枚举也非常适合 单身和实现默认行为,同时允许以后的定制(即 策略模式)。后者的一个例子是 java.nio.fileOpenOptionStandardOpenOption: 如果一个开发人员想创建他自己的非标准 OpenOption,他可以。

从技术上讲,人们确实可以将枚举看作是一个包含大量类型化常量的类,这实际上就是枚举常量在内部实现的方式。然而,使用 enum可以提供有用的方法(Enum javadoc) ,否则您必须自己实现这些方法,例如 Enum.valueOf

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

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

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

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

枚举的好处:

  1. 枚举是类型安全的,静态字段不是
  2. 有一个有限数量的值(不可能传递不存在的枚举值。如果有静态类字段,则可能出现这种错误)
  3. 每个枚举可以有多个属性(字段/getter)分配-封装。还有一些简单的方法: YEAR.toSecond ()或类似的方法。比较: Colors.RED.getHex ()和 Colors.toHex (Colors.RED)

“例如能够轻松地为枚举元素赋予一定的值”

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

“因此,无需大量努力就能将整数转换为枚举” 添加一个将 int 转换为 enum 的方法,只需添加包含映射的静态 HashMap < Integer,EnumX > 。

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

一个 enum 是隐式的 final,有一个私有构造函数,它的所有值都是相同类型或者子类型的,你可以使用 values()获得它的所有值,得到它的 name()或者 ordinal()值,或者你可以通过数字或者名字来查找一个 enum。

您还可以定义子类(即使是名义上的 final,这是您无法通过其他方式实现的)

enum Runner implements Runnable {
HI {
public void run() {
System.out.println("Hello");
}
}, BYE {
public void run() {
System.out.println("Sayonara");
}
public String toString() {
return "good-bye";
}
}
}


class MYRunner extends Runner // won't compile.

没有人提到在 switch语句中使用它们的能力; 我也要加上这一点。

这允许以一种干净的方式使用任意复杂的枚举,而无需使用 instanceof、可能混淆 if序列或非字符串/整数开关值。规范的示例是状态机。

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

更多信息来自 来源

这里有很多好的答案,但是没有一个提到 Collection API 类/接口 特别是枚举高度优化的实现:

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

这是什么意思?

如果我们的 Enum类型不超过64个元素(大多数现实生活中的 Enum例子都符合这个要求) ,那么实现将元素存储在一个单独的 long值中,每个有问题的 Enum实例将与这个64位长的 long的一部分相关联。向 EnumSet添加元素只是将适当的位设置为1,删除它只是将该位设置为0。测试一个元素是否在 Set中只是一个位掩码测试!你一定会喜欢 Enum的!

例如:

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) 没有有意义的印刷: 打印任何这些常量的值都会打印它的数值而不是有意义的硬币名称,例如,当你打印镍时,它会打印“5”而不是“镍”

3) 没有命名空间: 要访问 currencyDenom 常量,我们需要在类名前面加上前缀,例如 CurrencyDenom.PENNY,而不仅仅是使用 PENNY,尽管这也可以通过在 JDK 1.5中使用静态导入来实现

枚举的优势

1) Java 中的枚举是类型安全的,并且有自己的名称空间。这意味着您的枚举将有一个类型,例如“货币”在下面的例子,你不能指派任何值以外的指定的枚举常数。

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

货币硬币 = 货币 硬币 = 1;//编译错误

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

3)可以在创建时指定枚举常数的值,如下例所示: 公开枚举货币{ 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

  1. 类型安全和价值安全。
  2. 保证一对一。
  3. 定义和重写方法的能力。
  4. 无限制地使用 switch语句 case语句中的值的能力。
  5. 通过 ordinal().内置的值序列化
  6. 通过名称而不是通过值进行序列化,这在一定程度上可以防止将来出现问题。
  7. EnumSetEnumMap类。

另一个重要的区别是,Java 编译器将 基本类型绳子static final字段视为文本。这意味着这些常量成为内联的。它类似于 C/C++ #define预处理器。见 这个所以问题。枚举不是这种情况。

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

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

更多

在这里张贴的枚举有许多优点,我现在正在创建这样的枚举,正如问题中提到的那样。 但是我有一个包含5-6个字段的枚举。

enum Planet{
EARTH(1000000, 312312321,31232131, "some text", "", 12),
....
other planets
....

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

使用 static final常量初始化,并使用 Builder模式创建这样的对象使其更具可读性。但是,如果需要使用枚举,您将失去所有其他优势。 这种类的一个缺点是,需要手动将 Planet对象添加到 Planets.list/set

相对于这类类,我仍然更喜欢枚举,因为 values()非常方便,而且你永远不知道将来是否需要在 switchEnumSetEnumMap中使用它们:)

主要原因: 枚举可以帮助你编写结构良好的代码,其中参数的语义在编译时是清晰的,并且是强类型的——这是其他答案给出的所有原因。

等价交换: 在 Java 中,Enum 的成员数组是 final。这通常很好,因为它有助于重视安全性和测试,但在某些情况下,这可能是一个缺点,例如,如果您正在从库扩展现有的基本代码。相比之下,如果相同的数据位于具有静态字段的类中,则可以在运行时轻松地添加该类的新实例(可能还需要编写代码将这些实例添加到该类的任何 Iterable 中)。但是 Enums 的这种行为是可以改变的: 使用反射可以在运行时添加新成员或替换现有成员,尽管这可能只应该在没有其他选择的特定情况下进行: 例如,它是一个蹩脚的解决方案,可能会产生意想不到的问题,请参阅我在 我可以在 Java 运行时添加和删除枚举元素吗上的答案。

枚举可以是本地的

爪哇16开始,枚举可以在本地(在方法中)定义。此作用域还可以将枚举定义为嵌套类或单独的类。

这个新的本地定义范围附带了新的记录特性。详情请参阅 < em > JEP 395: Records 。枚举、接口和记录都可以在 Java16 + 本地定义。

相反,public static final字段总是具有全局作用域。

你可以这样做:

public enum Size { SMALL(1), MEDIUM(2), LARGE(3) };


private int sizeValue;


Size(sizeValue) {this.sizeValue = value; }

这样你就可以得到像 SMALL.getSizeValue ()这样的 size 值;

如果您想设置大小枚举不适合您,如果您将只定义常量和固定值是好的。

检查一下这个 链接也许能帮到你