One-liner用于实例化具有通用接口的枚举集合

时间:2014-01-10 03:29:28

标签: java generics collections enums

我有两个实现通用接口的枚举。我试图声明界面,所以只有枚举才能实现它。

interface CommonEnumInterface<E extends Enum<?>> {

    /** It doesn't matter what this does. */
    E commonInterfaceMethod();

}

enum Enum1 implements CommonEnumInterface<Enum1> {
    FOO,
    BAR,
    BAZ;

    @Override
    public Enum1 commonInterfaceMethod() {
        return null;
    }
}

enum Enum2 implements CommonEnumInterface<Enum2> {
    BLAH,
    YADDA,
    RHUBARB;

    @Override
    public Enum2 commonInterfaceMethod() {
        return Enum2.BLAH;
    }
}

现在我想创建另一个枚举,其中每个常量都有自己的一组CommonEnumInterface类型的元素,但不一定是同一个类。我发现的每一种方式都感觉像是一种黑客攻击,特别是对于我希望每个常量都用多组元素声明的情况。

这些评论显示了我的尝试。是否有更优雅的方式(编译)?

enum CreatingAnotherEnumAttempt {

    // Type mismatch: cannot convert from List<Enum<?>&CommonEnumInterface<?>> to List<CommonEnumInterface<?>>
    A(Arrays.asList(Enum1.FOO, Enum2.BLAH)),

    // Cannot cast from List<Enum<?>&CommonEnumInterface<?>> to List<CommonEnumInterface<?>>
    B((List<CommonEnumInterface<?>>) Arrays.asList(Enum1.FOO, Enum2.BLAH)),

    // This works, but it generates type safety warnings, and it's hard to read.
    C(Arrays.asList((CommonEnumInterface<?>) Enum1.FOO, (CommonEnumInterface<?>) Enum2.BLAH)),

    // This is the best I've come up with.
    D(getMixOfElements(Enum1.FOO, Enum2.BLAH));

    /**
     * I want to populate this set in the constructor.
     */
    private final Set<CommonEnumInterface<?>> mixOfElements;

    /**
     * I tried declaring the Set this way, but wildcards can extend only one interface, so there's a syntax error at the ampersand.
     */
    private final Set<? extends Enum<?> & CommonEnumInterface<?>> mixOfElements;

    /**
     * This is the constructor I'm trying to satisfy.
     *
     * @param mixOfElements I could make this a varargs parameter, but that only works for a single set of elements. What if I want more?
     */
    private CreatingAnotherEnumAttempt(List<CommonEnumInterface<?>> mixOfElements) {
        this.mixOfElements = Collections.unmodifiableSet(new HashSet<CommonEnumInterface<?>>(mixOfElements));
    }

    /**
     * This method only exists to allow method {@link #D}. It's pretty pointless, otherwise.
     *
     * @param mixOfElements the mix of elements
     * @return the mix of elements as a list
     */
    private static List<CommonEnumInterface<?>> getMixOfElements(CommonEnumInterface<?>... mixOfElements) {
        return Arrays.asList(mixOfElements);
    }

    /**
     * This works without type safety warnings or superfluous methods, but for my constructor, I need a one-liner.
     */
    private static void wayThatWorksInline() {
        Collection<CommonEnumInterface<?>> inlineWay = new ArrayList<CommonEnumInterface<?>>();
        inlineWay.add(Enum1.FOO);
        inlineWay.add(Enum2.BLAH);
    }

}

我的问题在上面,并在代码注释中。通常,这是声明我的界面的正确方法吗?

2 个答案:

答案 0 :(得分:1)

涉及枚举的事实实际上是一种转移,因为简单地将List<CommonEnumInterface<?>>传递给构造函数是您的目标。为此,请执行以下操作:

A(Arrays.<CommonEnumInterface<?>>asList(Enum1.FOO, Enum2.BLAH)),

//etc.

问题是类型推断 - 有时它会有点过于急切。根据参数Enum1.FOOEnum2.BLAHasList,编译器为Enum<?>&CommonEnumInterface<?>推断了T,因此返回了List<Enum<?>&CommonEnumInterface<?>>。自generics aren't covariant起,这不能分配给List<CommonEnumInterface<?>>。指定T的类型参数可以手动解决此问题。

答案 1 :(得分:-2)

我会尽力给这一点。您可以尝试使用动态类型。

List<CommonEnumInterface<dynamic>>
or
List<dynamic>

或者采用更加无声但干净的解决方案

public class MyEnums
{
    List<Tuple<Enum, string>> myEnums;
    public void addEnum(Enum Enum, string EnumName)
    {
        myEnums.Add(new Tuple<Enum, string>(Enum, EnumName));
    }

    public List<Tuple<Enum, string>> getEnums()
    {
        return myEnums;
    }

    public Enum getEnumByName(string EnumName)
    {
        return myEnums.Where(n => n.Item2 == EnumName).Select(n => n.Item1).FirstOrDefault();
    }
}

public class myEnums
{
    public dynamic s { get; set; }
}