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

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

enum E {
    A, B, C;
}

Как показано вэтот ответ отlucasmoЗначения enum хранятся в статическом массиве в том порядке, в котором они были инициализированы, и вы можете позже получить (клон) этот массив с помощью.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?

НОТА: я делаюне намереваться этот вопрос быть субъективным. Моя просьба о "Лучший" реализация - это условное обозначение запроса самой быстрой, чистой и удобной в обслуживании реализации.

 johnchen90209 июн. 2013 г., 05:53
E.C.getPrevious() == E.C или же ?E.C.getPrevious() == E.B
 wchargin09 июн. 2013 г., 05:54
@ johnchen902 исправлено; извиняюсь

Ответы на вопрос(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();
  }
}
</someenum>

Заметки:

В отличие от другого ответа, этот способ делает некоторыенеявный отображение; вместо того, чтобы полагаться наordinal(), Конечно, это означает больше кода; но это также заставляет авторарассматривать что значит добавить новые константы или удалить существующие. Полагаясь на порядковый, вашнеявный Предполагается, чтопорядок основан на порядке, используемом для объявления константы enum. Поэтому, когда кто-то возвращается через 6 месяцев и должен добавить новую константу, он должен понимать, что новая константа Y нуждается вX, Y, Z ... вместо того, чтобы просто добавить!X, Z, YМогут быть ситуации, когда это нене имеет никакого смысла для "прошлой" константа перечисления, чтобы иметь "первый" как преемник. Думайте о размерах футболки для примеров. XXL.next () точно не XS. Для таких ситуаций использование Optional является более подходящим ответом.
 wchargin03 янв. 2017 г., 14:13
Да уж. Я люблю то, чтоOptional можно было бы и дать ему шанс в более крупных проектах; Это'Жаль, что он плохо интегрируется с остальным языком.
 wchargin03 янв. 2017 г., 14:11
Привет, GhostCat. Я полагаю, что я не имелЯ прямо заявил об этом в вопросе, но я надеялся на автоматическое расширение (т.е. не "грубая сила"), исходя из предположения, что порядок по умолчанию является логичным. Я неЯ действительно не думаю, что это отвечает на вопрос, но ваше мнение о том, что порядок по умолчанию может быть нежелательным, действительно ценно.
 GhostCat03 янв. 2017 г., 14:12
Добро пожаловать; Я просто наткнулся на какой-то другой вопрос и подумал: особенно эта дополнительная часть будет стоить своего собственного ответа.
Решение Вопроса

Попробуй это:

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

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

РЕДАКТИРОВАТЬ: Как было предложено, сделать частную статическую копиюvalues() массив, чтобы избежать копирования массива каждый разnext() или жеprevious() называется.

 yshavit09 июн. 2013 г., 06:05
@WChargin Вы могли бы даже сделатьvalues поле статическое, так как онобудет одинаковым для всех значений перечисления.
 johnchen90209 июн. 2013 г., 06:10
Кстати, я скомпилировал код с Eclipse и разобрал с помощью javap. Это нет клон ноnew и затем я'System.arraycopy
 Jim Garrison09 июн. 2013 г., 06:01
Я был бы удивлен, если бы было какое-либо клонирование, поскольку значения enum являются синглетонами. Вы всегда можете посмотреть на сгенерированный код.
 yshavit09 июн. 2013 г., 06:14
@WChargin Если тыесли вы беспокоитесь об этом, то нестатическийТебе тоже не поможет. Кто-то может сделать то же самое и пойти в город, пройдя мимоE.A (или любые другие значения перечисления) вместоnull к первому аргументуField сеттера. Если ты'Вы беспокоитесь об отражении таким образом, вам нужно взять представление "удар" (что, я сомневаюсь, было бы в любом случае значительным, если только это не находится в очень узкой петле) клона каждый раз
 Jim Garrison09 июн. 2013 г., 06:13
Ну, вопрос в том, насколько параноиком ты хочешь быть ... чтотвоя цель?
 Joe28 янв. 2016 г., 20:28
Если у вас есть экземпляр «А» называется "а" такие как:A a, Затем назначьте это так:a = a.next();  Вызовa.next(); не будет увеличиваться само по себе. См богемныйответ вstackoverflow.com/questions/17664445/java-operator-for-enum
 wchargin09 июн. 2013 г., 06:02
Синглтон не делаетне имеет значения, когда вы могли бы сделатьA.values()[0] = null или жеA.values()[0] = A.Z и испортить все остальное. Просто потому, что массив является окончательным нет его содержание.
 Jim Garrison09 июн. 2013 г., 06:07
Я просто разобрал код и обнаружил, что он клонируется по причинам, указанным другими. Так что я'сделать копию один раз и использовать это.
 wchargin09 июн. 2013 г., 06:00
Ах! Я не былне знаю оordinal; я искалindexOf, Было бы лучше объявитьA[] values = values() избежать клонирования дважды?
 wchargin09 июн. 2013 г., 06:12
@ johnchen902 в любом случае этоперераспределение памяти. Метод неочень важно (хотяarraycopy может быть немного быстрее).
 wchargin09 июн. 2013 г., 06:01
values метод возвращает(A[]) ($VALUES.clone()) гдеprivate static final A[] $VALUES = new A[] {X,Y,Z}, Увидетьэтот ответ
 wchargin09 июн. 2013 г., 06:10
я не решаюсь объявитьprivate static final A[] vals когда ты могA.class.getField("vals").setAccessible(true) и идти в город ... насколько я знаю, рефлексия бессильна внутри метода, предполагая, что загрузчик классов / VM не имеетбыл скомпрометирован.
 yshavit09 июн. 2013 г., 06:22
Если кто-то имеет доступ к машине, на которой выполняется ваш Java-код, безопасность подвергается риску при любом его разрезании; они могут просто внедрить javaagent и переписать ваш байт-код во что угодно. Если они неУ него нет прямого доступа к нему, но вы разрешаете произвольные плагины, тогда вам определенно нужен какой-то менеджер безопасности. Кроме того, я бы поставил под сомнение выбор загрузки ненадежных сторонних плагинов в приложение на стороне сервера. Так что, в принципе, я бы нене слишком беспокоиться о безопасности с точки зрения отражения, потому что вы в основном неТого нет. :)

Ваш ответ на вопрос