Как написать общий ротатор перечисления Java? - PullRequest
12 голосов
/ 10 декабря 2011

Как сделать общий ротатор enum?В данном примере это будет общая версия next().

    public class TestEnum
    {
        enum Temperature { hot, cold };

        public static void main(String[] args)
        {
            Temperature t = Temperature.hot;
            Temperature t2 = next(t);
        }

        static Temperature next(Temperature t)
        {
            if (t.ordinal() == Temperature.values().length - 1)
                return Temperature.values()[0];
            else
                return Temperature.values()[t.ordinal() + 1];
        }
    }

Редактировать: В комментариях @irreputable предлагает превосходное решение.irreputable если вы отправите это как ответ, я выберу это.Вы можете сэкономить время, скопировав этот ответ.

static <T extends Enum<T>> T next(T t)
{
    int index = t.ordinal();
    @SuppressWarnings("unchecked")
    Enum<T>[] constants = t.getClass().getEnumConstants();
    if (index == constants.length - 1)
    {
        @SuppressWarnings("unchecked")
        T result = (T)constants[0];
        return result;
    }
    else
    {
        @SuppressWarnings("unchecked")
        T result = (T)constants[index + 1];
        return result;
    }
}

Ответы [ 6 ]

10 голосов
/ 10 декабря 2011

Попробуйте:

public class Test {

    enum Temperature { hot, cold };

        public static void main(String[] args) throws Exception
        {
            Temperature t = Temperature.hot;
            Temperature t2 = next(t);
            System.out.println(t2);
        }

        static <T extends Enum> T next(T t) throws Exception
        {
            Method values = t.getClass().getMethod("values");
            if (t.ordinal() == ((T[])values.invoke(t)).length - 1)
                return ((T[])values.invoke(t))[0];
            else
                return ((T[])values.invoke(t))[t.ordinal() + 1];
        }
}
5 голосов
/ 10 декабря 2011

IMO, полагаться на позицию, чтобы думать о «следующем» или «предыдущем», было бы плохой идеей, так же как плохой идеей полагаться на ordinal () в коде вашего приложения. Было бы более разумно, чтобы ваше перечисление определяло как вращение должно происходить так, чтобы добавление новых членов между ними не приводило к ошибкам.

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

Подход 1 (большая гибкость)

enum Direction1 {

    EAST() {
        @Override
        public Direction1 next(Rotation rotation) {
            return rotation == Rotation.CLOCKWISE ? SOUTH : NORTH;
        }
    },
    WEST() {
        @Override
        public Direction1 next(Rotation rotation) {
            return rotation == Rotation.CLOCKWISE ? NORTH : SOUTH;
        }
    },
    SOUTH() {
        @Override
        public Direction1 next(Rotation rotation) {
            return rotation == Rotation.CLOCKWISE ? WEST : EAST;
        }
    },
    NORTH() {
        @Override
        public Direction1 next(Rotation rotation) {
            return rotation == Rotation.CLOCKWISE ? EAST : WEST;
        }
    };

    abstract Direction1 next(Rotation rotation);

    static enum Rotation {
        CLOCKWISE, ANTICLOCKWISE
    };

}

Подход 2, более общий, меньше гибкости

interface Rotatable {
    // now it would be difficult to make next() method take a rotation
    Rotatable next();
}

enum Direction2 implements Rotatable {

    // assume clockwise rotation

    EAST() {
        @Override
        public Direction2 next() {
            return SOUTH;
        }
    },
    WEST() {
        @Override
        public Direction2 next() {
            return NORTH;
        }
    },
    SOUTH() {
        @Override
        public Direction2 next() {
            return WEST;
        }
    },
    NORTH() {
        @Override
        public Direction2 next() {
            return EAST;
        }
    };

}
0 голосов
/ 04 марта 2013
public <T extends Enum<T>> T rotate(T current, int increment) {
  T[] values = current.getDeclaringClass().getEnumConstants();
  int i = current.ordinal() + increment;
  while (i<0) i+= values.length; 
  return values[i%values.length];
}

Temperature next = rotate(hot, 1);
Temperature prev = rotate(hot, -1);
Temperature nextNext = rotate(hot, 2);
0 голосов
/ 28 июля 2012

Я считаю, что использование порядковых значений - это всегда хрупкий подход, так как он будет опираться на позицию, используйте values() или class.getEnumConstants().

0 голосов
/ 10 декабря 2011

Это не прямой ответ на вопрос, а скорее другой подход к тому, что вы пытаетесь сделать.

public interface<T extends Enum<T>> NextEnum {
    T next();
}

public enum Temperature implements NextEnum<Temperature> {
    hot, cold;

    public Temperature next() {
        if (ordinal() == Temperature.values().length - 1)
            return Temperature.values()[0];
        else
            return Temperature.values()[ordinal() + 1];
    }
}

Для всех перечислений, которые реализуют NextEnum, вы можете использовать метод next(). Например:

Temperature.hot.next();
0 голосов
/ 10 декабря 2011

Использование интерфейса:

public interface EnumInterface {
    public EnumInterface[] values1();
    public int ordinal1();
}   

enum Temperature implements EnumInterface {
    HOT,
    COLD;

    public EnumInterface[] values1() {
        return values();
    }

    public int ordinal1() {
        return ordinal();
    }
}

static <T extends EnumInterface> T next(T t) {      
    if (t.ordinal1() == t.values1().length - 1)
        return (T) t.values1()[0];
    else
        return (T) t.values1()[t.ordinal1() + 1];
} 

public static void main(String[] args) {
    Temperature t = Temperature.HOT;
    Temperature t2 = next(t);
    System.out.println(t2);
}
...