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

And here's one that will wrap any number of enums. It comes as an Iterator but combine it with either or both of the other solutions and I think you've got all you asked for.

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

Prints:

[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]

Note that Iterables.in merely wraps an Iterator<E> in an Iterable<E> like this (not my code - I found it here on 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 に答える