Каковы различия между перечислением Java и классом с помощью частного конструктора?
Я пытался понять, как Java enum действительно работает, и я пришел к выводу, что он очень похож на обычный 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;
}
}
Ответы
Ответ 1
Отличия:
- Enums расширяют
java.lang.Enum
и получают все приятные функции:
- Автоматическое поведение однопользовательского режима с помощью правильной сериализации
- Автоматический читаемый человеком метод
.toString
для значений перечисления без необходимости дублирования имен перечислений
-
.name
и .ordinal
специальные методы
- Используется в высокопроизводительных классах
EnumSet
и EnumMap
на основе биттетов
- Перечисления рассматриваются языком специально:
- В Enums используется специальный синтаксис, который упрощает создание экземпляра без написания десятков полей
public static final
- Перечисления могут использоваться в операторах
switch
- Перечисления не могут быть созданы вне списка перечислений, кроме как с помощью отражения
- Перечисления не могут быть расширены за пределами списка перечислений
- Java автоматически компилирует лишние вещи в перечисления:
-
public static (Enum)[] values();
-
public static (Enum) valueOf(java.lang.String);
-
private static final (Enum)[] $VALUES;
(values()
возвращает клон этого)
Большинство из них можно эмулировать с соответствующим классом, но Enum
просто упрощает создание класса с этим набором особенно желательных свойств.
Ответ 2
Взгляните на эту страницу в блоге, он описывает, как Java enum
скомпилированы в байт-код. Вы увидите, что есть небольшое дополнение по сравнению с вашим вторым образцом кода, который представляет собой массив объектов Direction
, называемых VALUES
. Этот массив содержит все возможные значения для вашего перечисления, поэтому вы не сможете сделать
new Direction(2, 2)
(например, используя отражение), а затем использовать это как допустимое значение Direction
.
Плюс, как правильно объясняет @Eng.Fouad, у вас нет values()
, valueOf()
и ordinal()
.
Ответ 3
Чтобы ответить на вопрос: по существу, нет разницы между этими двумя подходами. Тем не менее, конструкция enum предоставляет вам дополнительные вспомогательные методы, такие как values()
, valueOf()
и т.д., Которые вам придется писать самостоятельно с помощью подхода class-with-private-constructor.
Но мне нравится, как перечисления Java в основном похожи на любые другие классы в Java, у них могут быть поля, поведение и т.д. Но для меня то, что разделяет перечисления из простых классов, - это идея, что перечисления представляют собой классы/типы, экземпляры/члены заданы. В отличие от обычных классов, в которых вы можете создавать любое количество экземпляров, перечисления ограничивают создание только известных экземпляров. Да, как вы показали, вы также можете сделать это с помощью классов с частными конструкторами, но перечисления просто делают это более интуитивно понятным.
Ответ 4
Как указали люди, вы теряете 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
с этим классом; поэтому вы потеряете это.
Ответ 5
Основное отличие состоит в том, что каждый класс enum
неявно расширяет класс Enum<E extends Enum<E>>
. Это приводит к тому, что:
-
enum
объекты имеют такие методы, как name()
и ordinal()
-
enum
объекты имеют специальные реализации toString()
, hashCode()
, equals()
и compareTo()
-
enum
объекты подходят для оператора switch
.
Все перечисленные выше не применимы для вашей версии класса Direction
. Это различие "смысл".