Каковы различия между перечислением Java и классом с закрытым конструктором?
Я пытался понять, как на самом деле работает перечисление Java, и пришел к выводу, что он очень похож на обычный класс Java, конструктор которого объявлен как частный.
Я только что пришел к такому выводу, и он не основывается на раздумьях, но я хотел бы знать, что я что-то пропустил.
Ниже приведена реализация простого перечисления Java и эквивалентного класса Java.
public enum Direction {
ENUM_UP(0, -1),
ENUM_DOWN(0, 1),
ENUM_RIGHT(1, 0),
ENUM_LEFT(-1, 0);
private int x;
private int y;
private Direction(int x, int y){
this.x = x;
this.y = y;
}
public int getEnumX(){
return x;
}
public int getEnumY(){
return y;
}
}
В чем разница в значении кода выше и ниже?
public class Direction{
public static final Direction UP = new Direction(0, -1) ;
public static final Direction DOWN = new Direction(0, 1) ;
public static final Direction LEFT = new Direction(-1, 0) ;
public static final Direction RIGHT = new Direction(1, 0) ;
private int x ;
private int y ;
private Direction(int x, int y){
this.x = x ;
this.y = y ;
}
public int getX(){
return x;
}
public int getY(){
return y;
}
}
5 ответов
Отличия:
- Перечисления расширяются
java.lang.Enum
и получить все его приятные возможности:- Автоматическое одиночное поведение благодаря правильной сериализации
- Автоматически читаемый человеком
.toString
метод значений enum без необходимости дублировать имена enum .name
а также.ordinal
специальные методы- Используется в высокопроизводительных основанных на битсетах
EnumSet
а такжеEnumMap
классы
- Перечисления обрабатываются языком специально:
- Перечисления используют специальный синтаксис, который упрощает создание экземпляров без написания десятков
public static final
поля - Перечисления могут быть использованы в
switch
заявления - Перечисления не могут быть созданы вне списка перечисления, кроме как с помощью отражения
- Перечисления не могут быть расширены за пределы списка перечисления
- Перечисления используют специальный синтаксис, который упрощает создание экземпляров без написания десятков
- Java автоматически компилирует дополнительный материал в перечисления:
public static (Enum)[] values();
public static (Enum) valueOf(java.lang.String);
private static final (Enum)[] $VALUES;
(values()
возвращает клон этого)
Большинство из них можно эмулировать с соответствующим классом, но Enum
просто позволяет действительно легко создать класс с этим набором особенно желательных свойств.
Чтобы ответить на вопрос: по сути, нет никакой разницы между двумя подходами. Тем не менее, enum construct предоставляет вам несколько дополнительных вспомогательных методов, таких как values()
, valueOf()
и т. д., которые вы должны были бы написать самостоятельно с помощью подхода class-with-private-constructor.
Но да, мне нравится, что перечисления Java в основном похожи на любые другие классы в Java, они могут иметь поля, поведение и т. Д. Но для меня то, что отличает перечисления от простых классов, это идея, что перечисления являются классами / типами, чьи экземпляры / члены предопределены. В отличие от обычных классов, в которых вы можете создавать любое количество экземпляров, перечисления ограничивают создание только известными экземплярами. Да, как вы показали, вы можете сделать это с классами с закрытыми конструкторами, но перечисления просто делают это более интуитивно понятным.
Посмотрите на эту страницу блога, она описывает, как Java enum
s компилируются в байт-код. Вы увидите, что есть небольшое дополнение по сравнению со вторым примером кода, который представляет собой массив Direction
объекты называются VALUES
, Этот массив содержит все возможные значения для вашего перечисления, поэтому вы не сможете сделать
new Direction(2, 2)
(например, используя отражение), а затем использовать его в качестве действительного Direction
значение.
Плюс, как правильно объясняет @Eng.Fouad, у вас нет values()
, valueOf()
а также ordinal()
,
Как люди уже говорили, вы проигрываете values()
, valueOf()
а также ordinal()
, Вы можете легко воспроизвести это поведение, используя комбинацию Map
и List
,
public class Direction {
public static final Direction UP = build("UP", 0, -1);
public static final Direction DOWN = build("DOWN", 0, 1);
public static final Direction LEFT = build("LEFT", -1, 0);
public static final Direction RIGHT = build("RIGHT", 1, 0);
private static final Map<String, Direction> VALUES_MAP = new LinkedHashMap<>();
private static final List<Direction> VALUES_LIST = new ArrayList<>();
private final int x;
private final int y;
private final String name;
public Direction(int x, int y, String name) {
this.x = x;
this.y = y;
this.name = name;
}
private static Direction build(final String name, final int x, final int y) {
final Direction direction = new Direction(x, y, name);
VALUES_MAP.put(name, direction);
VALUES_LIST.add(direction);
return direction;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
public static Direction[] values() {
return VALUES_LIST.toArray(new Direction[VALUES_LIST.size()]);
}
public static Direction valueOf(final String direction) {
if (direction == null) {
throw new NullPointerException();
}
final Direction dir = VALUES_MAP.get(direction);
if (dir == null) {
throw new IllegalArgumentException();
}
return dir;
}
public int ordinal() {
return VALUES_LIST.indexOf(this);
}
@Override
public int hashCode() {
int hash = 7;
hash = 29 * hash + name.hashCode();
return hash;
}
@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final Direction other = (Direction) obj;
return name.equals(other.name);
}
@Override
public String toString() {
return name;
}
}
Как вы видете; код становится очень неуклюжим очень быстро.
Я не уверен, есть ли способ для репликации switch
утверждение с этим классом; так что вы потеряете это.
Основное отличие заключается в каждом enum
класс неявно расширяется Enum<E extends Enum<E>>
учебный класс. Это приводит к тому, что:
enum
объекты имеют такие методы какname()
а такжеordinal()
enum
объекты имеют специальныеtoString()
,hashCode()
,equals()
а такжеcompareTo()
реализацииenum
объекты подходят дляswitch
оператор.
Все вышеперечисленное не относится к вашей версии Direction
учебный класс. Это "смысловая" разница.