3

I have the following interface and class definition.

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

Now I have a class where I declare a Map and assign the enum as key and set a value.

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

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

Question: As you see myMap is tied to the enum C. I want to create a generic version of myMap, so I can assign any enum value in the class B.

I already went thru the stackoverflow post: How to implement enum with generics?

2
  • 2
    Why not just use Map<A, String> and new HashMap<>()? Commented Jan 18, 2017 at 21:46
  • @Andreas Maybe A has other implementations. Commented Jan 18, 2017 at 21:49

4 Answers 4

1

You can't do this with EnumMap.

EnumMap requires a key of a single type — the constructor is there to enforce this by tying the generic type to a concrete type.

Under the hood it builds a cache of possible key values that it uses to enforce run-time type safety.

You'll need to use another type of map if you want to allow keys from your hierarchy.

Sign up to request clarification or add additional context in comments.

Comments

0

Try this:

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

This puts a bound on the type to any Enum that implements A.

This is equivalent to <? extends Enum<?> & A>, but easier to type.

17 Comments

Really? Not ? extends Enum<?> & A? This doesn't look like it'd work because of the bounds on Enum.
@andy they are the same: see stackoverflow.com/questions/14122783/…
But can you put into a map with upper-bounded key type?
Cannot put() with Map<? extends Enum<? extends A>, String>. Just change to Map<Enum<? extends A>, String>. --- Of course, that's not enough, because myMap = new EnumMap<C, String>(C.class) won't work. That will then have to be myMap = new HashMap<>().
@Andreas Like I said, it means keys are not of type A. For example, A a = myMap.keySet().iterator().next(); is a compile error.
|
0

I would second what teppic said -- you cannot use EnumMap generically with all subclasses of A. It must be constructed with a specific concrete class. See the JavaDoc of EnumMap

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

I see that you seems to have two options here

Use other types of Map implementations (e.g. HashMap)

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

The output would be:

{c1=c1}

Implement yet another subclasses of A so that you can use EnumMap

If, for some reasons, you really want to use EnumMap, and you do not want to implement all the values of C, D, E into one enum, you still have the following solution.

Implement a new subclasses of A (let's call it SuperCde in the following code example) that has all the possible values of C, D, and E, and it has a static method getSuperCde() that serves as a bridge:

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

And then you can use the following code:

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

The output would be:

{c1=c1}

Comments

0

As pointed out by others you cannot use EnumMap. Also, while using <E extends Enum<E> & A> works, the version for bounded wildcards <? extends Enum<?> & A> does not exist. Given that, you could wrap your map key with an object that does the static type checking using the <E extends Enum<E> & A> construct.

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

}

Here is a demo:

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), "");

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.