2

我正在寻找一种为多个枚举构建包装器的方法。说你有

public enum Enum1 {
    A,B,C
}

public enum Enum2 {
    ONE,TWO,THREE
}

我想要一个带有文字的新枚举

(A,ONE), (A,TWO), (A,THREE), (B,ONE), ...

整个事情都是通用的,所以我不必知道 Enum1 和 Enum2。有没有办法构建它甚至将它扩展到 n Enums?

还是我应该寻找其他通用方法来建模?

4

4 回答 4

4

我会设置一个类来表示您所指的元组。如果你愿意,你可以在这里使用泛型:

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;
于 2012-11-28T16:50:21.933 回答
2

除了@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()。

于 2012-11-28T17:03:49.790 回答
2

这是一个可以包装任意数量的枚举。它是一个迭代器,但将它与其他解决方案中的一个或两个结合起来,我认为你已经得到了你所要求的一切。

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();
  }
}
于 2012-11-28T22:09:41.810 回答
0

我最近需要为参数化测试执行此操作。我首选的方法是使用 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()最终流。

于 2020-04-08T17:34:45.127 回答