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.Enum
inheritance,并且不能被子类化)。
是否有其他更基本的好处, enum
提供无法通过Typesafe Enum模式实现?
- “不能分类”不是限制。 这是一个很大的优势:它确保始终只有
enum
定义的值集合,而不再是! -
enum
正确处理序列化。 您也可以使用类型安全的枚举,但它经常被遗忘(或根本不知道)。 这确保了对于任何两个enum
值e1
和e2
,e1.equals(e2)
总是暗示e1 == e2
e2
(反之亦然,这可能更重要)。 - 有特定的轻量级数据结构来处理枚举:
EnumSet
和EnumMap
(从这个答案中窃取)
当然,其他人会在这里提到很多优点作为答案。 最重要的是,你可以非常快速地编写enums
并且它们执行许多操作,例如实现Serializable
, Comparable
, equals()
, 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
等)。
EnumSet
和EnumMap
是围绕枚举的特定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()
方法(可以实现EnumSet
和EnumMap
等高效数据结构),实现Serializable
,覆盖toString
,提供values()
和valueOf(String name)
。 它们可以用在类型安全开关语句中,并且是单例。