Java的枚举优于旧的“Typesafe Enum”模式?

在JDK1.5之前的Java中,“Typesafe Enum”模式是实现只能获取有限数量值的类型的常用方法:

public class Suit { private final String name; public static final Suit CLUBS =new Suit("clubs"); public static final Suit DIAMONDS =new Suit("diamonds"); public static final Suit HEARTS =new Suit("hearts"); public static final Suit SPADES =new Suit("spades"); private Suit(String name){ this.name =name; } public String toString(){ return name; } } 

(参见例如Bloch的Effective Java的第21项)。

现在在JDK1.5 +中,“官方”方式显然是使用enum

 public enum Suit { CLUBS("clubs"), DIAMONDS("diamonds"), HEARTS("hearts"), SPADES("spades"); private final String name; private Suit(String name) { this.name = name; } } 

显然,语法更好一些,更简洁(不需要显式定义值的字段,适用于提供的toString() ),但到目前为止enum看起来非常类似于Typesafe Enum模式。

我所知道的其他差异:

  • 枚举自动提供values()方法
  • 枚举可以在switch() (编译器甚至会检查你是否忘记了一个值)

但这一切看起来只不过是语法糖,甚至引入了一些限制(例如, enum总是从java.lang.Enuminheritance,并且不能被子类化)。

是否有其他更基本的好处, enum提供无法通过Typesafe Enum模式实现?

  • “不能分类”不是限制。 这是一个很大的优势:它确保始终只有enum定义的值集合,而不再是!
  • enum正确处理序列化。 您也可以使用类型安全的枚举,但它经常被遗忘(或根本不知道)。 这确保了对于任何两个enume1e2e1.equals(e2) 总是暗示e1 == e2 e2 (反之亦然,这可能更重要)。
  • 有特定的轻量级数据结构来处理枚举: EnumSetEnumMap (从这个答案中窃取)

当然,其他人会在这里提到很多优点作为答案。 最重要的是,你可以非常快速地编写enums并且它们执行许多操作,例如实现SerializableComparableequals()toString()hashCode()等,这些都没有包含在你的枚举中。

但我可以向你展示enum (IMO)的严重缺点 。 您不仅无法随意对它们进行子类化,而且还无法为它们配备通用参数。 当你写这个:

 // A model class for SQL data types and their mapping to Java types public class DataType { private final String name; private final Class type; public static final DataType INT = new DataType("int", Integer.class); public static final DataType INT4 = new DataType("int4", Integer.class); public static final DataType INTEGER = new DataType("integer", Integer.class); public static final DataType BIGINT = new DataType("bigint", Long.class); private DataType(String name, Class type){ this.name = name; this.type = type; } // Returns T. I find this often very useful! public T parse(String string) throws Exception { // [...] } } class Utility { // Enums equipped with generic types... public static  T doStuff(DataType type) { return ... } } 

这是枚举不可能的:

 // This can't be done public enum DataType { // Neither can this... INT("int", Integer.class), INT4("int4", Integer.class), // [...] } 

您的类型安全枚举实现有点过于简单。 处理序列化时,它会变得复杂得多。

Java enum解决了序列化/反序列化的问题。 enum的保证是唯一的,您可以将它们与==运算符进行比较。

阅读Effective Java 2nd Edition中的相应章节(关于使用枚举而不是单例,关于使用EnumSet等)。

EnumSetEnumMap是围绕枚举的特定function构建的自定义数据结构。 它们具有方便的额外function,而且非常快。 在没有枚举的情况下,没有相应的(至少没有相同的使用优雅,见评论)。

此外:

JDK5枚举可以很容易地用在具有良好IDE支持的switch-case语句中

 Suit suit = ...; switch (suit) { case SPADES: System.out.println("Motorhead!"); break; default: System.out.println("Boring .."); } 

单独使用句法糖是值得的:-P毕竟,这也是(:)的含义。

但严重的是,开箱即用自动命名()和ordinal(),枚举它们,在switch()中使用它们,为它们附加额外的值是很好的参数:它避免了大量的样板代码。

使用整数的传统懒惰替代方案不是类型安全的,而且更加有限。 枚举超过这个替代方案的缺点是它们不再轻量级。

现在在JDK1.5 +中,“官方”方式显然是使用enum

 public enum Suit { CLUBS("clubs"), DIAMONDS("diamonds"), HEARTS("hearts"), SPADES("spades"); private final String name; private Suit(String name) { this.name = name; } } 

实际上,它更像是

  public enum Suit { CLUBS, DIAMONDS, HEARTS, SPADES; } 

因为枚举已经提供了name()方法。 此外,它们提供了一个ordinal()方法(可以实现EnumSetEnumMap等高效数据结构),实现Serializable ,覆盖toString ,提供values()valueOf(String name) 。 它们可以用在类型安全开关语句中,并且是单例。