Java 的枚举相对于旧的“类型安全枚举”模式的优势?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/5092015/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-30 09:22:31  来源:igfitidea点击:

Advantages of Java's enum over the old "Typesafe Enum" pattern?

javadesign-patternsenumslanguage-design

提问by sleske

In Java prior to JDK1.5, the "Typesafe Enum" pattern was the usual way to implement a type that can only take a finite number of values:

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

(see e.g. Item 21 from Bloch's Effective Java).

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

Now in JDK1.5+, the "official" way is obviously to use enum:

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

Obviously, the syntax is a bit nicer and more concise (no need to explicitly define fields for the values, suitable toString()provided), but so far enumlooks very much like the Typesafe Enum pattern.

显然,语法更好、更简洁(不需要为值明确定义字段,toString()提供合适的),但到目前为止enum看起来非常像 Typesafe Enum 模式。

Other differences I am aware of:

我知道的其他差异:

  • enums automatically provide a values()method
  • enums can be used in switch()(and the compiler even checks that you don't forget a value)
  • 枚举自动提供一个values()方法
  • 枚举可以用于switch()(并且编译器甚至会检查您是否没有忘记一个值)

But this all looks like little more than syntactic sugar, with even a few limitations thrown in (e.g. enumalways inherits from java.lang.Enum, and cannot be subclassed).

但这一切看起来只不过是语法糖,甚至还有一些限制(例如,enum总是继承自java.lang.Enum,并且不能被子类化)。

Are there other, more fundamental benefits that enumprovides that could not be realized with the Typesafe Enum pattern?

还有其他更基本的好处是enumTypesafe Enum 模式无法实现的吗?

回答by Joachim Sauer

  • "cannot be subclassed" isn't a restriction. It's one of the big advantages: It ensures there there is always only ever exactly the set of values defined in the enumand no more!
  • enumcorrectly handles serialization. You cando that with type-safe enums as well, but it's often forgotten (or simply not known). This ensures that e1.equals(e2)alwaysimplies e1 == e2for any two enumvalues e1and e2(and vice versa, which is probably more important).
  • There are specific lightweight data structures that handle enums: EnumSetand EnumMap(stolen from this answer)
  • “不能被子类化”不是限制。这是一大优势:它确保始终只有在 中定义的一组值,enum而没有更多!
  • enum正确处理序列化。您也可以使用类型安全的枚举做到这一点,但它经常被遗忘(或根本不知道)。这确保对于任何两个值ande1.equals(e2)总是隐含(反之亦然,这可能更重要)。e1 == e2enume1e2
  • 有处理枚举的特定轻量级数据结构:EnumSetEnumMap(从这个答案中窃取)

回答by Lukas Eder

Of course there are lots of advantages other people will mention here as answers. Most importantly, you can write enumsvery fast and they do a lot of things like implement Serializable, Comparable, equals(), toString(), hashCode(), etc, which you didn't include in your enum.

当然,其他人会在这里提到许多优点作为答案。最重要的是,你可以写enums非常快,他们做了很多的事情一样实现SerializableComparableequals()toString()hashCode(),等你没在你的枚举包括。

But I can show you a serious drawbackof enum(IMO). Not only can't you subclass them at will, but you can't equip them with a generic parameter. When you could write this:

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

// A model class for SQL data types and their mapping to Java types
public class DataType<T> {
    private final String name;
    private final Class<T> type;

    public static final DataType<Integer> INT      = new DataType<Integer>("int", Integer.class);
    public static final DataType<Integer> INT4     = new DataType<Integer>("int4", Integer.class);
    public static final DataType<Integer> INTEGER  = new DataType<Integer>("integer", Integer.class);
    public static final DataType<Long>    BIGINT   = new DataType<Long>("bigint", Long.class);    

    private DataType(String name, Class<T> 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> T doStuff(DataType<T> type) {
        return ...
    }
}

This is not possible with an enum:

这对于枚举是不可能的:

// This can't be done
public enum DataType<T> {

    // Neither can this...
    INT<Integer>("int", Integer.class), 
    INT4<Integer>("int4", Integer.class), 

    // [...]
}

回答by meriton

Now in JDK1.5+, the "official" way is obviously to use enum:

public enum Suit {
  CLUBS("clubs"), DIAMONDS("diamonds"), HEARTS("hearts"), SPADES("spades");

  private final String name;

  private Suit(String name) {
    this.name = name;
  }
}

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

Actually, it's more like

其实更像是

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

because enums already provide a name()method. Additionally, they provide an ordinal()method (which enables efficient data structures like EnumSetand EnumMap), implement Serializable, override toString, provide values()and valueOf(String name). They can be used in a type safe switch statement, and are singletons.

因为枚举已经提供了一种name()方法。此外,它们提供了一个ordinal()方法(它可以启用像EnumSetand这样的高效数据结构EnumMap)、implement Serializable、override toString、providevalues()valueOf(String name)。它们可以用在类型安全的 switch 语句中,并且是单例。

回答by Roman

Your type-safe enum implementation is a bit oversimplified. When you deal with serialization it will become much more complicated.

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

Java enums solve the problem with serialization/deserialization. enum's are guarantied to be unique and you can compare them with ==operator.

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

Read correspondent chapters in Effective Java 2nd Edition (about using enums instead of singletons, about using EnumSets etc).

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

回答by Sean Patrick Floyd

EnumSetand EnumMapare custom data structures that are built around the specific features of enums. They have handy extra features and they are extremely fast. There is no equivalent (at least not with equivalent elegance of use, see comments) to them without enums.

EnumSet并且EnumMap是围绕枚举的特定功能构建的自定义数据结构。它们具有方便的额外功能,而且速度非常快。如果没有枚举,就没有等效的(至少没有等效的优雅使用,请参阅评论)。

回答by Heiko Rupp

In addition:

此外:

JDK5 enums can be easily used in switch-case statements with good IDE support

JDK5 枚举可以在具有良好 IDE 支持的 switch-case 语句中轻松使用

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

回答by PhiLho

Syntactic sugar alone is worth its salt :-P After all, that's what for ( : ) is, too.

语法糖本身就值得它的盐 :-P 毕竟,这也是 (:) 的意义所在。

But seriously, the fact to have automatically name() and ordinal() out of the box, to enumerate them, to use them in switch (), to attach additional values to them are good arguments for them: it avoids lot of boilerplate code.

但说真的,自动 name() 和 ordinal() 开箱即用,枚举它们,在 switch() 中使用它们,为它们附加附加值的事实对它们来说是很好的论据:它避免了大量样板代码.

The traditional lazy alternative, using ints, isn't typesafe and is much more limited. A drawback of enums over this alternative is that they are no longer lightweight.

使用 int 的传统惰性替代方案不是类型安全的,而且受到的限制要大得多。枚举相对于这种替代方案的一个缺点是它们不再是轻量级的。