java 如何找到EnumMap中使用的枚举?

iaqfqrcu  于 2022-12-25  发布在  Java
关注(0)|答案(3)|浏览(120)

我有一个测试类,它有一个内部枚举和一个EnumMap,如下所示。

enum MyEnum { ENUM1, ENUM2 }
EnumMap<MyEnum, String> emap = new EnumMap<>( MyEnum.class );
public void discover() {
  Class<?> eclass = ???;
  System.out.println( eclass );
}

肩印
MyEnum.class

rt4zxlrg

rt4zxlrg1#

如果Map不为空,可以检索一个键并检查其类,否则只能通过反射访问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的内部实现发生变化,这可能会中断,最好找到一种方法来满足您的需求。

mmvthczy

mmvthczy2#

如果EnumMap不为空,则可以使用

map.keySet().iterator().next().getDeclaringClass();

如果ENUM包含具有特定于常量的实现的方法,则getClass()不起作用。
如果EnumMap为空,则可以使用反射,如@assylias的答案所示。
另一种方法是子类化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; }
}
7lrncoxx

7lrncoxx3#

如果你不能将EnumMap子类化,序列化为一个ObjectOutputStream,并用一个特殊的ObjectInputStream反序列化,这个特殊的ObjectInputStreamEnumMap类描述符解析为你自己的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()

相关问题