通用枚举与泛型类型枚举

3
我有以下接口和类定义。
interface A
{
}

class B
{
    public static enum C implements A
    {
        c1, c2, c3;
    }

    public static enum D implements A
    {
        d1, d2, d3;
    }

    public static enum E implements A
    {
        e1, e2, e3;
    }
}

现在我有一个类,在其中声明了一个Map,并将枚举设为键并设置一个值。

class Test
{
    private Map<C, String> myMap;

    public void assignVal()
    {
        myMap = new EnumMap<C, String>(C.class);
        myMap.put(C.c1, String.valueOf(1));
    }
}

问题:你可以看到myMap与枚举C相关联。我想创建一个泛型版本的myMap,这样我就可以在类B中分配任何枚举值。

我已经查阅了stackoverflow上的文章:如何实现带有泛型的枚举?


2
为什么不直接使用 Map<A, String>new HashMap<>() 呢? - Andreas
@Andreas 可能 A 有其他的实现。 - shmosel
4个回答

1

使用EnumMap无法完成这个任务。

EnumMap要求键的类型必须是相同的——构造函数通过将泛型类型与具体类型绑定来实现此目的。

在内部,它建立了一个可能键值的缓存,用于强制执行运行时类型安全性。

如果您想允许从您的层次结构中选择键,则需要使用另一种类型的映射。


0

试试这个:

private Map<Enum<? extends A>, String> myMap;

这将把类型限制为实现 A 接口的任何枚举。

这相当于 <? extends Enum<?> & A>,但更容易输入。


1
真的吗?不是? extends Enum<?> & A吗?这看起来不像会工作,因为在Enum上有限制。 - Andy Turner
2
但是你能把它放到一个具有上限键类型的映射中吗? - Andy Turner
1
无法使用 Map<? extends Enum<? extends A>, String> 进行 put() 操作。只需更改为 Map<Enum<? extends A>, String> 即可。当然,这还不够,因为 myMap = new EnumMap<C, String>(C.class) 也行不通。那么就需要将其更改为 myMap = new HashMap<>() - Andreas
@AndyTurner 谁说一定要用 EnumMap - shmosel
1
@Andreas 就像我说的那样,这意味着键不是类型 A。例如,A a = myMap.keySet().iterator().next(); 是一个编译错误。 - shmosel
显示剩余12条评论

0
我赞同teppic所说的 -- 你不能将EnumMap泛型地应用于A的所有子类。它必须使用具体的实现类进行构造。请参阅EnumMap的JavaDoc。

https://docs.oracle.com/javase/7/docs/api/java/util/EnumMap.html

我看到你似乎有两个选择

使用其他类型的Map实现(例如HashMap

Map<A, String> myMap1 = new HashMap<>();
myMap1.put(C.c1, C.c1.name());
System.out.println(myMap1);

输出将会是:

{c1=c1}

实现另一个A的子类,以便您可以使用EnumMap

如果出于某些原因,您确实想使用EnumMap,并且您不想将CDE的所有值都实现为一个枚举,您仍然有以下解决方案。

实现A的新子类(在下面的代码示例中称为SuperCde),该子类具有CDE的所有可能值,并且它具有一个静态方法getSuperCde()作为桥梁:

public static enum SuperCde implements A{
    c1,c2,c3,
    d1,d2,d3,
    e1,e2,e3
    ;

    public static SuperCde getSuperCde(A a) {
        if (a instanceof C) {
            C cValue = (C) a;
            switch (cValue) {
            case c1: return SuperCde.c1;
            case c2: return SuperCde.c2;
            case c3: return SuperCde.c3;
            default: throw new IllegalArgumentException();              
            }
        } else if (a instanceof D) {
            D dValue = (D) a;
            switch (dValue) {
            case d1: return SuperCde.d1;
            case d2: return SuperCde.d2;
            case d3: return SuperCde.d3;
            default: throw new IllegalArgumentException();              
            }
        } else if (a instanceof E) {
            E eValue = (E) a;
            switch (eValue) {
            case e1: return SuperCde.e1;
            case e2: return SuperCde.e2;
            case e3: return SuperCde.e3;
            default: throw new IllegalArgumentException();              
            }
        } else {
            throw new IllegalArgumentException();
        }
    }
}

然后您可以使用以下代码:

Map<SuperCde, String> myMap2 = new EnumMap<SuperCde, String>(SuperCde.class);       
myMap2.put(SuperCde.getSuperCde(C.c1), C.c1.name());
System.out.println(myMap2);

输出将会是:

{c1=c1}

0
如其他人所指出,你不能使用EnumMap。另外,在使用 & A>时是有效的,但是对于有界通配符< ? extends Enum & A>并不存在。鉴于此,你可以使用 & A>来包装映射键,并进行静态类型检查。
class EnumAWrapper{
    final Enum<?> enumObj;
    final A aObj;

    <E extends Enum<E> & A> EnumAWrapper(E enumA){
        enumObj = enumA;
        aObj = enumA;
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((aObj == null) ? 0 : aObj.hashCode());
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        EnumAWrapper other = (EnumAWrapper) obj;
        if (aObj == null) {
            if (other.aObj != null)
                return false;
        } else if (!aObj.equals(other.aObj))
            return false;
        return true;
    }

}

这里有一个演示:

Map<EnumAWrapper, String> map = new HashMap<>();

//does compile
map.put(new EnumAWrapper(C.c1), "");
map.put(new EnumAWrapper(D.d1), "");
map.put(new EnumAWrapper(E.e1), "");
A aObj = map.keySet().iterator().next().aObj;
Enum<?> enumObj = map.keySet().iterator().next().enumObj;

//does not compile (given that enum F does not implement A)
map.put(new EnumAWrapper(new A(){}), "");
map.put(new EnumAWrapper(F.f1), "");

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