0

Хорошая ли практика использовать порядковый номер enum?

10

У меня есть перечисление (enum):

public enum Persons {

    CHILD,
    PARENT,
    GRANDPARENT;

}

Есть ли какие-либо проблемы с использованием метода ordinal() для проверки "иерархии" между членами перечисления? Я имею в виду - есть ли какие-то недостатки при использовании этого метода, кроме избыточности, когда кто-то может случайно изменить порядок в будущем?

Или лучше сделать что-то подобное:

public enum Persons {

    CHILD(0),
    PARENT(1),
    GRANDPARENT(2);

    private Integer hierarchy;

    private Persons(final Integer hierarchy) {
        this.hierarchy = hierarchy;
    }

    public Integer getHierarchy() {
        return hierarchy;
    }

}

Какие подходы вы бы порекомендовали в данном случае?

5 ответ(ов)

0

TLDR: Нет, не стоит этого делать!

Если вы обратитесь к javadoc для метода ordinal в Enum.java, то найдете следующее:

Большинство программистов не будут использовать этот метод. Он предназначен для использования в сложных структурах данных на основе перечислений, таких как java.util.EnumSet и java.util.EnumMap.

Во-первых, прочитайте документацию (в данном случае javadoc).

Во-вторых, не пишите хрупкий код. Значения перечисления могут измениться в будущем, и ваш второй пример кода гораздо более понятен и поддерживаем.

Вы определенно не хотите создавать проблемы в будущем, если, скажем, новое значение перечисления будет вставлено между PARENT и GRANDPARENT.

0

Как предложил Джошуа Блох в книге Effective Java, не стоит извлекать значение, связанное с перечислением (enum), из его порядкового номера (ordinal), так как изменения в порядке значений перечисления могут нарушить заложенную вами логику.

Второй подход, который вы упоминаете, точно соответствует предложению автора, заключающемуся в том, чтобы хранить значение в отдельном поле.

Я бы сказал, что предложенная вами альтернатива определенно лучше, так как она более расширяема и удобна в поддержке. Вы отделяете порядок значений перечисления от понятия иерархии.

0

Первый способ не совсем очевиден, так как вам нужно читать код, в котором используются перечисления (enums), чтобы понять, что порядок значений перечисления имеет значение. Это очень подвержено ошибкам.

public enum Persons {

    CHILD,
    PARENT,
    GRANDPARENT;

}

Второй способ лучше, так как он самообъясняем:

CHILD(0),
PARENT(1),
GRANDPARENT(2);

private SourceType(final Integer hierarchy) {
    this.hierarchy = hierarchy;
}

Разумеется, порядок значений перечисления должен соответствовать иерархическому порядку, указанному в аргументах конструктора перечисления.

Это вводит некую избыточность, так как и значения перечисления, и аргументы конструктора перечисления передают информацию об их иерархии.

Но почему это может быть проблемой?

Перечисления предназначены для представления констант и значений, которые не меняются часто.

Пример использования перечисления в вопросе хорошо иллюстрирует правильное использование перечислений:

CHILD, PARENT, GRANDPARENT

Перечисления не предназначены для представления значений, которые часто изменяются. В этом случае использование перечислений, вероятно, не является лучшим выбором, так как это может часто ломать клиентский код, который их использует, и, кроме того, это требует повторной компиляции, упаковывания и развертывания приложения каждый раз, когда изменяется значение перечисления.

0

Если вам нужно создать отношения между значениями перечисления (enum), можно использовать следующий прием с другими значениями перечисления:

public enum Person {
  GRANDPARENT(null),
  PARENT(GRANDPARENT),
  CHILD(PARENT);

  private final Person parent;

  private Person(Person parent) {
    this.parent = parent;
  }

  public final Person getParent() {
    return parent;
  }
}

Обратите внимание, что вы можете использовать только те значения перечисления, которые были объявлены в коде раньше, чем то, которое вы пытаетесь объявить. Это означает, что такой подход будет работать только в тех случаях, когда ваши отношения образуют ациклический ориентированный граф (и порядок их объявления соответствует корректной топологической сортировке).

0

Использование ordinal() не рекомендуется, так как изменения в объявлении перечисления могут повлиять на значения порядковых номеров.

ОбНОВЛЕНИЕ:

Стоит отметить, что поля перечисления являются константами и могут иметь дублирующиеся значения, например:

enum Family {
    OFFSPRING(0),
    PARENT(1),
    GRANDPARENT(2),
    SIBLING(3),
    COUSING(4),
    UNCLE(4),
    AUNT(4);

    private final int hierarchy;

    private Family(int hierarchy) {
        this.hierarchy = hierarchy;
    }

    public int getHierarchy() {
        return hierarchy;
    }
}

В зависимости от того, что вы собираетесь делать с hierarchy, это может быть как вредным, так и полезным.

Более того, вы можете использовать константы перечисления для создания своих собственных EnumFlags, вместо использования EnumSet, например:

Чтобы ответить на вопрос, пожалуйста, войдите или зарегистрируйтесь