Преимущества перечисления Java по сравнению со старым шаблоном «Typesafe Enum»? - PullRequest
26 голосов
/ 23 февраля 2011

В Java до JDK1.5 паттерн «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;
    }
}

(см., Например, пункт 21 из Эффективная Java * Блоха ).

Теперь в 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 очень похоже на шаблон Enpes Typesafe.

Другие различия Япомните:

  • перечисления автоматически предоставляют values() метод
  • перечисления могут использоваться в switch() (и компилятор даже проверяет, что вы не забыли значение)

Но все это выглядит немного больше, чем синтаксический сахар, с добавлением даже нескольких ограничений (например, enum всегда наследуется от java.lang.Enum и не может быть разделено на подклассы).

Существуют ли другие, более фундаментальные преимущества, которые enum предоставляет, которые нельзяealized с помощью шаблона Typesafe Enum?

Ответы [ 7 ]

15 голосов
/ 23 февраля 2011
  • «нельзя разделить на подклассы» не является ограничением. Это одно из больших преимуществ: оно гарантирует, что всегда существует только один и тот же набор значений, определенный в enum, и не более!
  • enum правильно обрабатывает сериализацию. Вы можете делать это и с безопасными типами перечислениями, но об этом часто забывают (или просто не знают). Это гарантирует, что e1.equals(e2) всегда подразумевает e1 == e2 для любых двух enum значений e1 и e2 (и наоборот, что, вероятно, более важно).
  • Существуют определенные легкие структуры данных, которые обрабатывают перечисления: EnumSet и EnumMap (украдено у этого ответа )
8 голосов
/ 23 февраля 2011

Конечно, есть много преимуществ, которые другие люди упомянут здесь как ответы. Самое главное, вы можете написать enums очень быстро, и они делают много вещей, таких как: Serializable, Comparable, equals(), toString(), hashCode() и т. Д., Которые вы не включили в свой ENUM.

Но я могу показать вам серьезный недостаток из 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), 

    // [...]
}
3 голосов
/ 26 февраля 2011

Теперь в 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).Они могут использоваться в операторе безопасного переключения типа и являются одиночными.

3 голосов
/ 23 февраля 2011

Ваша реализация enum с типобезопасным типом немного упрощена.Когда вы будете иметь дело с сериализацией, это станет намного сложнее.

Java enum s решает проблему с сериализацией / десериализацией.enum гарантированно являются уникальными, и вы можете сравнить их с оператором ==.

Прочитайте соответствующие главы в Effective Java 2nd Edition (об использовании перечислений вместо одиночных, об использовании EnumSet с и т. Д.).

2 голосов
/ 24 февраля 2011

Только синтаксический сахар стоит своей соли :-P В конце концов, это то, что для (:) тоже.

А если серьезно, то факт, что автоматически вызываются name () и ordinal () изполе, чтобы перечислять их, использовать их в switch (), добавлять к ним дополнительные значения, является хорошим аргументом для них: он избегает большого количества стандартного кода.

Традиционная ленивая альтернатива, использующая int, небезопасен и намного более ограничен.Недостаток перечислений над этой альтернативой состоит в том, что они больше не являются легкими.

2 голосов
/ 23 февраля 2011

Кроме того:

Перечисления JDK5 могут быть легко использованы в операторах switch-case с хорошей поддержкой IDE

Suit suit = ...; 
switch (suit) { 
    case SPADES: System.out.println("Motorhead!"); break;
    default: System.out.println("Boring ..");
}
2 голосов
/ 23 февраля 2011

EnumSet и EnumMap - это пользовательские структуры данных, построенные на основе специфических особенностей перечислений.У них есть удобные дополнительные функции, и они очень быстрые.Не существует эквивалента (по крайней мере, не с эквивалентной элегантностью использования, см. Комментарии) без перечислений.

...