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

sle*_*ske 26 java enums design-patterns language-design

在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;
    }
}
Run Code Online (Sandbox Code Playgroud)

(参见例如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;
  }
}
Run Code Online (Sandbox Code Playgroud)

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

我所知道的其他差异:

  • 枚举自动提供values()方法
  • 枚举可用于switch()(并且编译器甚至会检查您是否忘记了值)

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

是否有其他更基本的好处,enum使用Typesafe Enum模式无法实现?

Joa*_*uer 15

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


Luk*_*der 8

当然,其他人会在这里提到很多优点作为答案.最重要的是,你可以写enums非常快,他们做了很多的事情一样实现Serializable,Comparable,equals(),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 ...
    }
}
Run Code Online (Sandbox Code Playgroud)

这是枚举不可能的:

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

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

    // [...]
}
Run Code Online (Sandbox Code Playgroud)