如何编写一个通用的Java枚举旋转器?

12
我如何创建一个通用的枚举器?它将是这个例子中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提出了一种更好的解决方案。如果你把它发表为答案,我会选择它。你可能想节省时间,可以复制这个答案。

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

尝试:

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];
        }
}

作为通用旋转器,我认为这是一个很好的答案。我将使用此答案,再加上@SuppressWarnings("unchecked")<T extends Enum<?>>来定义类型,并明确捕获NoSuchMethodExceptionInvocationTargetExceptionIllegalAccessException,如果发生异常,我会抛出RuntimeException,因为我认为枚举旋转是低风险的。 - H2ONaCl
使用 Class.getEnumConstants() - irreputable

5

我认为,仅依靠位置来考虑“下一个”或“上一个”可能不是一个好主意,就像在应用程序代码中依赖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
    };

}

第二种方法更通用,但灵活性较低。

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

我认为使用序数值总是一种脆弱的方法,因为它将依赖于位置,要么使用values()class.getEnumConstants()


0

使用接口:

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);
}

1
这对于 .ordinal() 没问题,但在 .values() 上编译不通过。 - H2ONaCl
@broiyan,显然Enum类本身没有定义这样的方法。 - Tudor
是的,但Temperature有.values()方法。 - H2ONaCl
一个方法如何可以是通用的,同时使用单个类的特定方法? - Jens Schauder
@Tudor:如果没有调用values()的能力,这是行不通的。我有一种感觉,由于Java对泛型的破坏性实现,这个问题是无法解决的。 - Hovercraft Full Of Eels
显示剩余3条评论

0

这不是对问题的直接回答,而是对你尝试做的事情的另一种方法。

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
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);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接