Java 枚举相对于旧的“类型安全枚举”的优点图案?

发布于 2024-10-19 09:13:11 字数 1227 浏览 4 评论 0原文

在 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 中的 Item 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,并且不能被子类化)。

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

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:

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

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

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 enum looks very much like the Typesafe Enum pattern.

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)

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

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

看春风乍起 2024-10-26 09:13:11
  • “不能子类化”不是限制。这是最大的优点之一:它确保始终只有 enum 中定义的一组值,仅此而已!
  • enum 正确处理序列化。您也可以使用类型安全枚举来做到这一点,但它经常被忘记(或根本不知道)。这可确保对于任意两个 enum 值,e1.equals(e2) 始终 意味着 e1 == e2 >e1e2 (反之亦然,这可能更重要)。
  • 有一些处理枚举的特定轻量级数据结构:EnumSetEnumMap(从这个答案
  • "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 enum and no more!
  • enum correctly handles serialization. You can do that with type-safe enums as well, but it's often forgotten (or simply not known). This ensures that e1.equals(e2) always implies e1 == e2 for any two enum values e1 and e2 (and vice versa, which is probably more important).
  • There are specific lightweight data structures that handle enums: EnumSet and EnumMap (stolen from this answer)
雾里花 2024-10-26 09:13:11

当然,还有很多其他人会在这里提到的优点作为答案。最重要的是,您可以非常快地编写枚举,并且它们可以执行很多操作,例如实现SerializedComparableequals()< /code>、toString()hashCode() 等,您没有将其包含在枚举中。

但我可以向您展示 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 can't be done
public enum DataType<T> {

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

    // [...]
}

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

But I can show you a serious drawback of 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:

// 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), 

    // [...]
}
始终不够 2024-10-26 09:13:11

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

公共枚举套装{
  CLUBS("梅花")、DIAMONDS("方块")、HEARTS("红心")、SPADES("黑桃");

  私有最终字符串名称;

  私人西装(字符串名称){
    this.name = 名称;
  }
}

实际上,这更像是

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

因为枚举已经提供了name()方法。此外,它们还提供了ordinal()方法(该方法支持高效的数据结构,如EnumSet和EnumMap),实现Serialized >,覆盖toString,提供values()valueOf(String name)。它们可以用在类型安全的 switch 语句中,并且是单例。

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

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 EnumSet and EnumMap), implement Serializable, override toString, provide values() and valueOf(String name). They can be used in a type safe switch statement, and are singletons.

飘然心甜 2024-10-26 09:13:11

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

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

阅读《Effective Java 第二版》中的相应章节(关于使用枚举而不是单例,关于使用 EnumSet 等)。

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.

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

你是年少的欢喜 2024-10-26 09:13:11

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

EnumSet and EnumMap are 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.

独享拥抱 2024-10-26 09:13:11

另外:

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

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

In addition:

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

Suit suit = ...; 
switch (suit) { 
    case SPADES: System.out.println("Motorhead!"); break;
    default: System.out.println("Boring ..");
}
荒岛晴空 2024-10-26 09:13:11

语法糖本身就有价值 :-P 毕竟, ( : ) 也是如此。

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

传统的惰性替代方案(使用整数)不是类型安全的,而且受到更多限制。
与此替代方案相比,枚举的缺点是它们不再是轻量级的。

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

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.

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文