Подтвердить что ты не робот

Какой лучший способ реализовать `next` и` previous` в типе перечисления?

Предположим, что у меня есть перечисление:

enum E {
    A, B, C;
}

Как показано в этом ответе lucasmo, значения перечисления хранятся в статическом массиве в порядок их инициализации, и вы можете позже получить (клон) этого массива с помощью E.values().

Теперь предположим, что я хочу реализовать E#getNext и E#getPrevious так, чтобы все следующие выражения оценивались как true:

E.A.getNext() == E.B
E.B.getNext() == E.C
E.C.getNext() == E.A

E.A.getPrevious() == E.C
E.B.getPrevious() == E.A
E.C.getPrevious() == E.B

Моей текущей реализацией для getNext является следующее:

public E getNext() {
    E[] e = E.values();
    int i = 0;
    for (; e[i] != this; i++)
        ;
    i++;
    i %= e.length;
    return e[i];
}

и аналогичный метод для getPrevious.

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

Каким будет лучший способ реализовать методы getNext и getPrevious для типов перечислений в Java 7?


ПРИМЕЧАНИЕ: Я не намерен задавать этот вопрос субъективным. Моя просьба о "наилучшей" реализации - это сокращение, требующее, чтобы реализация была самой быстрой, чистой и удобной для обслуживания.

4b9b3361

Ответ 1

Попробуйте следующее:

public static enum A { 
    X, Y, Z;
    private static A[] vals = values();
    public A next()
    {
        return vals[(this.ordinal()+1) % vals.length];
    }

Реализация previous() остается как упражнение, но напомним, что в Java, modulo a % b может возвращать отрицательное число.

EDIT: как было предложено, сделайте приватную статическую копию массива values(), чтобы избежать копирования массива каждый раз, когда вызывается next() или previous().

Ответ 2

В качестве альтернативы можно как-то идти по строкам следующей идеи:

public enum SomeEnum {
  A, B, C;

  public Optional<SomeEnum> next() {
    switch (this) {
      case A: return Optional.of(B);
      case B: return Optional.of(C);
      // any other case can NOT be mapped!
      default: return Optional.empty();
  }
}

Примечания:

  • В отличие от другого ответа, этот способ показывает некоторое неявное отображение; вместо того, чтобы полагаться на ordinal(). Конечно, это означает больше кода; но это также заставляет автора считать, что значит добавлять новые константы или удалять существующие. Если полагаться на порядковый номер, ваше предположение неявное заключается в том, что порядок основан на порядке, используемом для объявления константы enum. Поэтому, когда кто-то возвращается через 6 месяцев и должен добавить новую константу, он должен понимать, что для новой константы Y требуется X, Y, Z... вместо просто добавления X, Z, Y!
  • Бывают ситуации, когда для "последней" константы перечисления нет смысла иметь "первый" как преемник. Подумайте о размерах футболок для примеров. XXL.next() - это точно не XS. Для таких ситуаций более подходящим ответом является использование опции "Дополнительно".