Перечисления в Java 5+ в основном представляют собой классы, которые имеют предопределенный набор экземпляров. Они предназначены для замены, скажем, набора целочисленных констант. Желательно, чтобы они были постоянными, поскольку они могут обеспечивать безопасность типов.
Так что вместо:
public class Suit {
public final static int SPADES = 1;
public final static int CLUBS = 2
public final static int HEARTS = 3;
public final static int DIAMONDS = 4;
}
у вас есть:
public enum Suit {
SPADES, CLUBS, HEARTS, DIAMONDS
}
Преимущества:
- Тип безопасности. Вы можете объявить аргумент функции, возвращаемый тип, член класса или локальную переменную как определенный тип Enum, и компилятор обеспечит безопасность типов;
- Перечисления в основном классы. Они могут реализовывать интерфейсы, иметь поведение и т. Д.
Безопасность типов является проблемой, поскольку в первом примере это допустимые операторы:
int i = Suit.DIAMONDS * Suit.CLUBS;
или вы можете перейти в 11 к функции, ожидающей иска. Вы не можете сделать это с типизированным перечислением.
Вы можете использовать класс для Suit для обеспечения безопасности типов, и это было решением до Java 5. Джош Блох (в Effective Java , который должен читать для программистов Java imho) продвигал тип перечисления типобезопасности, который стал перечислением Java 5+. Он содержит довольно много шаблонов, и некоторые важные случаи, которые люди не склонны обслуживать, такие как сериализация, не вызывающая конструктор, и чтобы убедиться, что у вас есть только один экземпляр, вы должны переопределить метод readResolve (). *
Например:
public enum CardColour {
RED, BLACK
}
public enum Suit {
SPADES(CardColour.BLACK),
CLUBS(CardColour.BLACK),
HEARTS(CardColour.RED),
DIAMONDS(CardColour.RED);
private final CardColour colour;
Suit(CardColour colour) { this.colour = colour; }
public CardColour getColour() { return colour; }
}
Редактировать: У Солнца есть введение в типизированные перечисления .
Что касается интерфейсов, они действительно дополняют перечисления, а не являются альтернативой. Как вы могли бы сказать, что Suit - это интерфейс, и у вас будет это:
public interface Suit {
CardColour getColour();
}
Проблема в том, что вы можете определить 300 разных мастей, а также несколько раз. Другим преимуществом перечислений является (несмотря на то, что регистр загружается в углу) то, что существует только один экземпляр каждого значения перечисления. Обычно это называется каноническим значением , что означает, что это равенство выполняется:
a.equals(b) == b.equals(a) == (a == b)
для всех a, b, которые являются экземплярами определенного Enum. Это означает, что вместо записи:
if (card.getSuit().equals(Suit.SPADES)) { ... }
Вы можете написать:
if (card.getSuit() == Suit.SPADES) { ... }
, который быстрее и обычно легче для чтения. Кроме того, IDE обычно дают вам обратную связь, если вы сравниваете перечисления разных типов, говоря, что они не могут быть равными, что может быть полезной и ранней формой проверки ошибок.