我正在寻找一种为多个枚举构建包装器的方法。说你有
public enum Enum1 {
A,B,C
}
public enum Enum2 {
ONE,TWO,THREE
}
我想要一个带有文字的新枚举
(A,ONE), (A,TWO), (A,THREE), (B,ONE), ...
整个事情都是通用的,所以我不必知道 Enum1 和 Enum2。有没有办法构建它甚至将它扩展到 n Enums?
还是我应该寻找其他通用方法来建模?
我会设置一个类来表示您所指的元组。如果你愿意,你可以在这里使用泛型:
public class EnumTupleImpl<E1 extends Enum<E1>, E2 extends Enum<E2>> {
private final E1 e1;
private final E2 e2;
public EnumTupleImpl(E1 e1, E2 e2) {
this.e1 = e1;
this.e2 = e2;
}
public E1 getE1() {
return e1;
}
public E2 getE2() {
return e2;
}
}
用法:
EnumTupleImpl<Enum1, Enum2> tupe1 = new EnumTupleImpl<Enum1, Enum2>(Enum1.A, Enum2.ONE);
EnumTupleImpl<Enum1, Enum2> tupe2 = new EnumTupleImpl<Enum1, Enum2>(Enum1.A, Enum2.TWO);
EnumTupleImpl<Enum1, Enum2> tupe3 = new EnumTupleImpl<Enum1, Enum2>(Enum1.A, Enum2.THREE);
您还可以通过枚举实例表示每个元组,如下所示:
public enum Tuple {
AONE(Enum1.A, Enum2.ONE),
ATWO(Enum1.A, Enum2.TWO),
ATHREE(Enum1.A, Enum2.THREE);
private Enum1 e1;
private Enum2 e2;
private EnumTupleEnum(Enum1 e1, Enum2 e2) {
this.e1 = e1;
this.e2 = e2;
}
public Enum1 getE1() {
return e1;
}
public Enum2 getE2() {
return e2;
}
}
用法:
Tuple.AONE.getE1(); //returns A
Tuple.AONE.getE2(); //returns ONE
对您来说可能有意义的是使用通用接口来包装枚举表示和类表示,如下所示:
public interface EnumTuple<E1, E2> {
E1 getE1();
E2 getE2();
}
这将允许您互换使用类或枚举:
public class EnumTupleImpl<E1 extends Enum<E1>, E2 extends Enum<E2>> imlements EnumTyple<E1, E2>{
...
}
public enum Tuple implements EnumTuple<Enum1, Enum2>{
...
}
用法:
EnumTuple<Enum1, Enum2> tupe1 = new EnumTupleImpl<Enum1, Enum2>(Enum1.A, Enum2.ONE);
EnumTuple<Enum1, Enum2> enum1 = Tuple.AONE;
除了@johncarl 的回答,您还可以尝试:
Class<E1> enumType1 =...
Class<E2> enumType2 = ...
List<EnumTuple<E1, E2>> enumTuples = new ArrayList<>(enumType1.getEnumConstants().length * enumType2.getEnumConstants().length);
//or
Set<EnumTuple<E1, E2>> enumTuples = new HashSet<>(enumType1.getEnumConstants().length * enumType2.getEnumConstants().length);
for (E1 e1 : enumType1.getEnumConstants()){
for (E2 e2 : enumType2.getEnumConstants()){
enumTuples.add(new EnumTuple<>(e1, e2));
}
}
如果您使用 HashSet,请不要忘记实现 equals 和 hashCode()。
这是一个可以包装任意数量的枚举。它是一个迭代器,但将它与其他解决方案中的一个或两个结合起来,我认为你已经得到了你所要求的一切。
public class Test {
public static void main(String args[]) {
new Test().test();
}
public static class EnumIterator implements Iterator<Enum[]> {
// The enums
private final Enum[][] enums;
// Where we are in each column.
private final int[] is;
// Which column to increment next.
private int i = 0;
// Construct from Enum[]s.
public EnumIterator(Enum[]... enums) {
// Grab the enums.
this.enums = enums;
// Start all ordinals at zero.
is = new int[enums.length];
// Next one to increment is the last one.
i = enums.length - 1;
}
// Construct from classes.
public EnumIterator(Class<? extends Enum>... classes) {
this(enumsFromClasses(classes));
}
// Factory to build the Enum[] array from an array of classes.
private static Enum[][] enumsFromClasses(Class<? extends Enum>[] classes) {
Enum[][] theEnums = new Enum[classes.length][];
for ( int j = 0; j < classes.length; j++ ) {
theEnums[j] = classes[j].getEnumConstants();
}
return theEnums;
}
@Override
public boolean hasNext() {
// We stop when we are about to increment col 0 and we are at its end.
return (i > 0 || is[0] < enums[0].length);
}
@Override
public Enum[] next() {
if (hasNext()) {
// One from each.
Enum[] next = new Enum[enums.length];
for (int j = 0; j < next.length; j++) {
next[j] = enums[j][is[j]];
}
// Step - Kinda like incrementing a number with each digit in a different base.
// Walk back past '9's setting them to 0.
for (i = is.length - 1; i > 0 && is[i] == enums[i].length - 1; i--) {
// Back one.
is[i] = 0;
}
// Step that one up one.
is[i] += 1;
return next;
} else {
throw new NoSuchElementException();
}
}
@Override
public void remove() {
throw new UnsupportedOperationException("Not supported.");
}
}
enum ABC {
A, B, C;
}
enum XY {
X, Y;
}
enum IJ {
I, J;
}
private void test() {
// Also works - but constructing from classes is cleaner.
//Iterator<Enum[]> i = new EnumIterator(ABC.values(), XY.values(), IJ.values());
Iterator<Enum[]> i = new EnumIterator(ABC.class, XY.class, IJ.class);
for (Enum[] e : Iterables.in(i)) {
System.out.println(Arrays.toString(e));
}
}
}
印刷:
[A, X, I]
[A, X, J]
[A, Y, I]
[A, Y, J]
[B, X, I]
[B, X, J]
[B, Y, I]
[B, Y, J]
[C, X, I]
[C, X, J]
[C, Y, I]
[C, Y, J]
请注意,Iterables.in
仅将一个包装成Iterator<E>
这样的Iterable<E>
(不是我的代码 - 我在 SO 上找到它)。
public class Iterables {
/**
* Adapts an {@link Iterator} to an {@link Iterable} for use in enhanced for loops.
*
* If {@link Iterable#iterator()} is invoked more than once, an
* {@link IllegalStateException} is thrown.
*/
public static <T> java.lang.Iterable<T> in(final Iterator<T> iterator) {
assert iterator != null;
class SingleUseIterable implements java.lang.Iterable<T> {
private boolean used = false;
@Override
public Iterator<T> iterator() {
if (used) {
throw new IllegalStateException("SingleUseIterable already invoked");
}
used = true;
return iterator;
}
}
return new SingleUseIterable();
}
}
我最近需要为参数化测试执行此操作。我首选的方法是使用 Java8 流和平面图:
enum Color {
RED,
BLUE;
}
enum Shape {
CIRCLE,
SQUARE;
}
enum Size {
BIG,
SMALL;
}
public static Stream<Object[]> data() {
return Arrays.stream(Color.values()).flatMap(color -> {
return Arrays.stream(Shape.values()).flatMap(shape -> {
return Arrays.stream(Size.values()).map(size -> new Object[] {color, shape, size});
});
});
}
如果需要,您可以随时调用.toArray()
最终流。