Reputation: 61
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?
Upvotes: 3
Views: 539
Reputation: 2789
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), "");
Upvotes: 0
Reputation: 7152
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}
Upvotes: 0
Reputation: 7286
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.
Upvotes: 1
Reputation: 424983
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.
Upvotes: 0