我有一个带有内部枚举和EnumMap的测试类,如下所示。如何查找EnumMap中使用的枚举?
enum MyEnum { ENUM1, ENUM2 }
EnumMap<MyEnum, String> emap = new EnumMap<>( MyEnum.class );
public void discover() {
Class<?> eclass = ???;
System.out.println( eclass );
}
应该打印
MyEnum.class
如果映射不为空,则可以检索一个键并检查其类。否则,唯一的方法是通过反射访问EnumMap的keyType
私有字段(该字段不通过公共API公开)。
在第一种情况下,你可以做:
Iterator<?> it = map.keySet().iterator();
while (it.hasNext()) {
Object key = it.next();
if (key != null) return key.getDeclaringClass();
}
在第二种情况下(显然也适用于非空枚举),您可以使用:
private static Class<?> enumMapType(EnumMap<?, ?> map) {
try {
Field keyType = EnumMap.class.getDeclaredField("keyType");
keyType.setAccessible(true);
return (Class<?>) keyType.get(map);
} catch (IllegalAccessException | NoSuchFieldException e) {
throw new AssertionError("Could not find EnumMap type", e);
}
}
如果EnumMap
的内部实现发生变化,这可能会中断,最好能找到绕过您需求的方法。
如果EnumMap
不为空,则可以使用
map.keySet().iterator().next().getDeclaringClass();
如果枚举具有具有常量特定实现的方法,则getClass()
不起作用。
如果EnumMap
为空,则可以使用反射,如@assilias的回答所示。
另一种选择是子类EnumMap
并编写一个public
getter。
public final class MyEnumMap<K extends Enum<K>, V> extends EnumMap<K, V> {
private final Class<K> keyType;
public MyEnumMap(Class<K> keyType) {
super(keyType);
this.keyType = keyType;
}
public Class<K> keyType() { return keyType; }
}
如果您不能将EnumMap
子类化,则序列化为ObjectOutputStream
并使用专用的ObjectInputStream
进行反序列化可以帮助您将EnumMap
类描述符解析为自己的EnumMap
子类。您的子类必须具有与java.util.EnumMap
相同的serialVersionUID
,并且必须具有简单名称EnumMap
,不能是内部类,但可以存在于任何包中。
下面是这样一个EnumMap
子类的例子:
package your.package.name.here;
import java.io.ObjectInputStream;
/**
* A serialization-compatible mock for {@link java.util.EnumMap} that uses an equal {@link #serialVersionUID} and a
* compatible set of fields. When de-serializing an {@link java.util.EnumMap} instance into an instance of this class,
* e.g., by overriding {@link ObjectInputStream}'s {@link ObjectInputStream#resolveClass} method such that it delivers
* this class instead of {@link java.util.EnumMap}, the fields are made accessible through getters; in particular,
* {@link #getKeyType()} reveals the original {@link java.util.EnumMap}'s key type, even if the map is empty.<p>
*
* The {@link EnumMap#getEnumMapKeyType(java.util.EnumMap)} method can be used to determine the key type of any
* {@link java.util.EnumMap}, even if it's empty.
*/
class EnumMap<K extends Enum<K>, V> extends java.util.EnumMap<K, V> {
private final Class<K> keyType;
private transient K[] keyUniverse;
private transient Object[] vals;
private transient int size = 0;
private static final long serialVersionUID = 458661240069192865L;
EnumMap(Class<K> c) {
super(c);
keyType = null;
}
public K[] getKeyUniverse() {
return keyUniverse;
}
public Object[] getVals() {
return vals;
}
public int getSize() {
return size;
}
public Class<K> getKeyType() {
return keyType;
}
/**
* Reconstitute the <tt>EnumMap</tt> instance from a stream (i.e., deserialize it).
*/
private void readObject(java.io.ObjectInputStream s) throws java.io.IOException, ClassNotFoundException {
// Read in the key type and any hidden stuff
s.defaultReadObject();
// Read in size (number of Mappings)
int size = s.readInt();
// Read the keys and values, and put the mappings in the HashMap
for (int i = 0; i < size; i++) {
s.readObject(); // key
s.readObject(); // value
}
}
}
注意字段的公共getter。这样,您就可以实现一个小实用程序,它提供专门的ObjectInputStream,然后从专门的EnumMap实例中获取密钥类型,如下所示:
package your.package.name.here;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.ObjectStreamClass;
public class EnumMapUtil {
private static class MyObjectInputStream extends ObjectInputStream {
public MyObjectInputStream(InputStream in) throws IOException {
super(in);
}
@Override
protected Class<?> resolveClass(ObjectStreamClass desc) throws IOException, ClassNotFoundException {
final Class<?> result;
if (desc.getName().equals("java.util.EnumMap")) {
result = your.package.name.here.EnumMap.class;
} else {
result = super.resolveClass(desc);
}
return result;
}
}
public static <K extends Enum<K>, V> Class<K> getKeyType(java.util.EnumMap<K, V> enumMap) throws IOException, ClassNotFoundException {
final Class<K> result;
if (enumMap.isEmpty()) {
final ByteArrayOutputStream bos = new ByteArrayOutputStream();
final ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(enumMap);
oos.close();
final ObjectInputStream ois = new MyObjectInputStream(new ByteArrayInputStream(bos.toByteArray()));
@SuppressWarnings("unchecked")
final your.package.name.here.EnumMap<K, V> readMap = (your.package.name.here.EnumMap<K, V>) ois.readObject();
final Class<K> keyType = readMap.getKeyType();
result = keyType;
} else {
result = enumMap.keySet().iterator().next().getDeclaringClass();
}
return result;
}
}
然后,您应该能够使用类似EnumMapUtil.getKeyType(myEmptyEnumMap)
的东西,它返回枚举的Class
对象,然后您可以在该对象上调用getEnumConstants()
。