如何通过其属性获取枚举类型?

我写了一个枚举类,我想要按类型获取属性并获取type by属性,但似乎不可能。

public enum AreaCode { area1(7927), area2(7928), area3(7929); private final int ac; AreaCode(int ac) { this.ac = ac; } int areaCode(){ return ac; } AreaCode area(int n) { switch (n) { case 7927: return AreaCode.area1; case 7928: return AreaCode.area2; case 7929: return AreaCode.area3; } } } 

上面的代码不会编译。 如何使area(int n)工作?

您需要做的就是添加一个默认大小写,以便该方法始终返回一些exception或引发exception:

 AreaCode area(int n){ switch (n) { case 7927: return AreaCode.area1; case 7928: return AreaCode.area2; case 7929: return AreaCode.area3; default: return null; } } 

或者更好

 AreaCode area(int n){ switch (n) { case 7927: return AreaCode.area1; case 7928: return AreaCode.area2; case 7929: return AreaCode.area3; default: throw new IllegalArgumentException(String.valueOf(n)); } } 

除了其他海报所指出的问题外,我会重写方法以避免重复信息( 保持干燥 !):

 public static AreaCode area(int n) { for (AreaCode c : values()) { if (c.ac == n) { return c; } } // either throw the IAE or return null, your choice. throw new IllegalArgumentException(String.valueOf(n)); } 

正如他们所说,有一种方法可以给猫皮肤。 首先,枚举值应该是大写的(由下划线分隔的单词),因为它们是常量值,并且应该由Java命名约定来处理。 至少,它们应该以大写字母开头,因为所有类名都应该。

 public enum AreaCode { AREA_1(7927), AREA_2(7928), AREA_3(7929); private int areaCode; private AreaCode(int areaCode) { this.areaCode = areaCode; } public int getAreaCode() { return areaCode; } } 

现在,有三种方法可以通过实例变量检索枚举。 switch语句,具有相等条件的循环和查找映射。 最后一个场景可能会为您的程序添加更多内存,但如果您需要快速查找大量枚举,这将有助于您以恒定的速率O(1)时间执行此操作。

下面的每个枚举类都是相同的,但每个类都在内部做了不同的事情。 通过将以下main()方法添加到这些类中的任何一个,您将获得相同的结果。

 public static void main(String[] args) { System.out.println(retrieveByAreaCode(7928)); } 

上面的例子将打印:

 AreaCode[name="AREA_2", value="7928"] 

开关

查找是O(1)(常数时间),但您需要对每个案例进行硬编码(不是非常动态)。

 public enum AreaCode { AREA_1(7927), AREA_2(7928), AREA_3(7929); private int areaCode; private AreaCode(int areaCode) { this.areaCode = areaCode; } public int getAreaCode() { return areaCode; } public static AreaCode retrieveByAreaCode(int n) { switch (n) { case 7927: return AreaCode.AREA_1; case 7928: return AreaCode.AREA_2; case 7929: return AreaCode.AREA_3; default: return null; } } @Override public String toString() { return String.format("%s[name=\"%s\", value=\"%d\"]", this.getClass().getName(), this.name(), this.getAreaCode()); } } 

查找是O(n)(线性时间),因此您需要遍历每个值,直到找到匹配项,但您确实需要对每个案例进行硬编码(动态)。

 public enum AreaCode { AREA_1(7927), AREA_2(7928), AREA_3(7929); private int areaCode; private AreaCode(int areaCode) { this.areaCode = areaCode; } public int getAreaCode() { return areaCode; } public static AreaCode retrieveByAreaCode(int n) { for (AreaCode areaCode : AreaCode.values()) { if (areaCode.getAreaCode() == n) { return areaCode; } } return null; } @Override public String toString() { return String.format("%s[name=\"%s\", value=\"%d\"]", this.getClass().getName(), this.name(), this.getAreaCode()); } } 

抬头

查找是O(1)(常量时间),您不需要对每个值进行硬编码(动态),但您需要存储占用内存的映射。

 import java.util.Collections; import java.util.HashMap; import java.util.Map; public enum AreaCode { AREA_1(7927), AREA_2(7928), AREA_3(7929); private static final Map LOOKUP_MAP; private int areaCode; static { LOOKUP_MAP = new HashMap(); for (AreaCode areaCode : AreaCode.values()) { LOOKUP_MAP.put(areaCode.getAreaCode(), areaCode); } LOOKUP_MAP = Collections.unmodifiableMap(LOOKUP_MAP); } private AreaCode(int areaCode) { this.areaCode = areaCode; } public int getAreaCode() { return areaCode; } public static AreaCode retrieveByAreaCode(int n) { return LOOKUP_MAP.get(n); } @Override public String toString() { return String.format("%s[name=\"%s\", value=\"%d\"]", this.getClass().getName(), this.name(), this.getAreaCode()); } } 

通用方法

EnumUtils.java

 import java.util.Collections; import java.util.HashMap; import java.util.Map; public class EnumUtils { public static interface EnumProperty, U> { U getValue(T type); } public static , U> Map createLookup(Class enumTypeClass, EnumProperty prop) { Map lookup = new HashMap(); for (T type : enumTypeClass.getEnumConstants()) { lookup.put(prop.getValue(type), type); } return Collections.unmodifiableMap(lookup); } } 
 import java.util.Map; public enum AreaCode { AREA_1(7927), AREA_2(7928), AREA_3(7929); private static final EnumUtils.EnumProperty ENUM_PROP; private static final Map LOOKUP_MAP; static { ENUM_PROP = new EnumUtils.EnumProperty() { @Override public Integer getValue(AreaCode code) { return code.getAreaCode(); } }; LOOKUP_MAP = EnumUtils.createLookup(AreaCode.class, ENUM_PROP); } private int areaCode; private AreaCode(int areaCode) { this.areaCode = areaCode; } public int getAreaCode() { return areaCode; } public static AreaCode retrieveByAreaCode(int n) { return LOOKUP_MAP.get(n); } @Override public String toString() { return String.format("%s[name=\"%s\", value=\"%d\"]", this.getClass().getName(), this.name(), this.getAreaCode()); } } 

我建议添加静态地图,将整数映射到区号,然后只使用这个地图。

 public enum AreaCode { area1(7927), area2(7928), area3(7929); private final int ac; private static Map id2code = new HashMap(); AreaCode(int ac) { this.ac = ac; id2code.put(ac, this); } int areaCode(){ return ac; } AreaCode area(int n){ return id2code.get(n); } } } 

它不编译的原因是缺少return语句。 您只返回被识别的案例的内容。 我建议你添加一个默认情况,返回一些表明区号不知道的东西。 名称unknown或null的枚举常量可以完成这项工作。

该方法应该是静态的,并且应该在每种情况下返回一些东西。 使它在默认情况下返回null,或者使它抛出IllegalArgumentException(或其他一些exception):这取决于你。

注意:阅读编译器错误消息应该指导您。

这是另一种方法(使用Guava和Java 8)为查找创建Map:

 import com.google.common.collect.Maps; public enum AreaCode { area1(7927), area2(7928), area3(7929); private final int ac; private final static Map AREA_BY_CODE = Maps.uniqueIndex(EnumSet.allOf(AreaCode.class), AreaCode::areaCode); AreaCode(int ac) { this.ac = ac; } public static AreaCode area(int n) { return AREA_BY_CODE.get(n); } int areaCode() { return ac; } } 

我编写了一个帮助程序,以避免污染枚举代码,并允许您按属性获取任何类型的枚举。 您将不再需要在每个枚举类型上声明特定方法。

在您的情况下,您可以像下面一样使用它(您的getter必须是公共的):

 // Java 8 AreaCode area = FunctionalEnumHelper.getEnum(AreaCode.class, AreaCode::areaCode, 7927); // value is area1 // Java 6 AreaCode area = EnumHelper.getEnum(AreaCode.class, 7927); // value is area1 

细节:

鉴于以下枚举:

 public enum Move { FORWARD("F"), RIGHT("R"), LEFT("L"); private String label; private Move(String label) { this.label = label; } public String getLabel() { return label; } } 

助手

使用Java 8:使用函数式编程

 import java.util.function.Function; /** * Helper to get an {@link Enum} of any Type by attribute or method * */ public final class FunctionalEnumHelper { // Constructors //------------------------------------------------- /** * Private constructor * A helper should not be instantiated in order to force static calls */ private FunctionalEnumHelper() {} // Static methods //------------------------------------------------- /** * Get the enum of type E associated to the attribute * @param enumType * @param method * @param expectedValue * @return */ public static , R> E getEnum(final Class enumType, final Function method, final R expectedValue) { E enumVariable = null; E[] values = enumType.getEnumConstants(); if(values != null) { for(E e : values) { if(e != null) { Object value = method.apply(e); if(value == null && expectedValue == null || value != null && value.equals(expectedValue)) { enumVariable = e; break; } } } } return enumVariable; } /* Functional style */ public static , R> E getEnum(final Class enumType, final Function method, final R expectedValue) { return Arrays.stream(enumType.getEnumConstants()) .filter(e -> { final Object value = method.apply(e); return value == null && expectedValue == null || value != null && value.equals(expectedValue); }) .findAny() .orElse(null); } } 

使用:

 Move move = FunctionalEnumHelper.getEnum(Move.class, Move::getLabel, "F") // value is Move.FORWARD 

使用Java 6:使用reflectionAPI

 import java.lang.annotation.ElementType; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Helper to get an {@link Enum} of any Type by attribute or method * */ public final class EnumHelper { private static final Logger logger = LoggerFactory.getLogger(EnumHelper.class); // Constructors //------------------------------------------------- /** * Private constructor * A helper should not be instantiated in order to force static calls */ private EnumHelper() {} // Static methods //------------------------------------------------- /** * Get the enum of type E which first attribute has value {@link obj}. * @param enumType * @param value * @return */ public static > E getEnum(final Class enumType, final Object value) { return getEnum(enumType, null, value); } /** * Get the enum of type E which attribute {@link attributeName} has value {@link obj}. * @param enumType * @param value * @return */ public static > E getEnum(final Class enumType, final String attributeName, final Object value) { return getEnum(enumType, ElementType.FIELD, attributeName, value); } /** * Get the enum of type E associated to the attribute * @param enumType * @param elementType * @param name * @param value * @return */ public static > E getEnum(final Class enumType, final ElementType elementType, final String name, final Object value) { E enumVariable = null; E[] enumObjs = enumType.getEnumConstants(); if(enumObjs != null) { ReflectionData reflectionData = new ReflectionData(); for(E enumObj : enumObjs) { if(enumObj != null) { Object val = getValue(reflectionData, elementType, name, enumObj); if(val == null && value == null || val != null && val.equals(value)) { enumVariable = enumObj; break; } } } } return enumVariable; } private static Object getValue(final ReflectionData reflectionData, final ElementType type, final String name, final Object obj) { Object value = null; final String parameter = name != null ? name.trim() : null; switch(type) { case FIELD : value = getFieldValue(reflectionData, obj, parameter); break; case METHOD : value = callMethod(reflectionData, obj, parameter); break; default : throw new IllegalArgumentException("The elementType '" + type.toString() + "' is not allowed!"); } return value; } /** * Get the attribute value * @param reflectionData * @param obj * @param fieldName * @return */ private static Object getFieldValue(final ReflectionData reflectionData, final Object obj, final String fieldName) { Object value = null; try { Field field = reflectionData.getField(); if(field == null) { field = computeField(obj, fieldName); reflectionData.setField(field); } boolean accessible = field.isAccessible(); field.setAccessible(true); value = field.get(obj); field.setAccessible(accessible); } catch (NoSuchFieldException | SecurityException e) { logger.error("No attribute {} : {}", fieldName, e.getMessage()); } catch (IllegalArgumentException | IllegalAccessException e) { logger.error(e.getMessage()); } return value; } private static Field computeField(final Object obj, final String fieldName) throws NoSuchFieldException { Field field = null; if(fieldName != null) { field = obj.getClass().getDeclaredField(fieldName); } else { Field[] fields = obj.getClass().getDeclaredFields(); if(fields != null) { int position = obj.getClass().getEnumConstants().length; field = fields[position]; } } return field; } /** * Call the method * @param reflectionData * @param obj * @param methodName * @return */ private static Object callMethod(final ReflectionData reflectionData, final Object obj, final String methodName) { Object returnValue = null; try { Method method = reflectionData.getMethod(); if(method == null) { method = obj.getClass().getMethod(methodName); reflectionData.setMethod(method); } returnValue = method.invoke(obj); } catch (SecurityException | NoSuchMethodException e) { logger.error("No method {} : {}", methodName, e.getMessage()); } catch (IllegalArgumentException | IllegalAccessException | InvocationTargetException e) { logger.error("Error when calling method {} on class {} : {}", methodName, obj.getClass(), e.getMessage()); } return returnValue; } private static class ReflectionData { private Field field; private Method method; public Field getField() { return field; } public Method getMethod() { return method; } public void setField(final Field field) { this.field = field; } public void setMethod(final Method method) { this.method = method; } } } 

使用:

 // Basic use Move move = EnumHelper.getEnum(Move.class, "F"); // value is Move.FORWARD 

您还可以指定要使用的属性(默认情况下,它是Enum的第一个属性)

 // Get by attribute Move move = EnumHelper.getEnum(Move.class, "label", "F"); // Get by method Move move = EnumHelper.getEnum(Move.class, ElementType.METHOD, "getLabel", "F"); 

优点

代码是集中的 ,您不需要对每个枚举编码相同的处理。 不要重新发明轮子!易于使用 ,可提高生产效率,并保持清洁效果

缺点

执行时间:复杂度为O(n) ,因此不如访问枚举类型(即O(1))中声明的静态哈希映射。 否则,因为它使用reflectionAPI(java 6)或Functional(java 8),所以性能比标准代码片段慢。 从规模来看,它更昂贵

但是可以添加缓存系统(EhCache,map ..)。

安全性:如果使用错误的参数调用exception,则此助手可以在运行时抛出exception,而标准代码在编译期间会产生错误。


性能测试

reflectionAPI很 ,所以它不适合生产! 不要在有时间限制的生产环境中使用它。

只需添加一个静态方法Move :: getMove进行测试比较:

 public enum Move { FORWARD("F"), RIGHT("R"), LEFT("L"); private String label; private Move(final String label) { this.label = label; } public String getLabel() { return label; } // Only used by regular test public static Move getMove(final String label) { Move move = null; for(Move curr : Move.values()) { if(curr.label.equals(label)) { move = curr; break; } } return move; } } 

现在我们可以比较每个解决方案的性能:

 public class Main { public static void main(final String[] args) { int nbrIterations = 1000000; doTrivial(nbrIterations); doRegular(nbrIterations); doFunctional(nbrIterations); doReflection(nbrIterations); } private static void doTrivial(final int nbrIterations) { long start = System.currentTimeMillis(); for (int i=0; i 

结果是:琐碎:28ms | 常规:53ms | function:171ms | 反思(论点):890ms | reflection(方法):800ms

这个基准测试显示function解决方案比常规解决方案稍贵(在枚举中有丑陋的代码......),但它仍然可以接受。 具有reflection的解决方案很漂亮,但它不适合具有时间限制的环境。

您应该在switch语句中包含一个default子句,因为当n不是7927,7928或7929时,编译器不会解决该做什么。

我希望它有所帮助。

你可以使用下一个结构

 public enum AreaCode { area1(7927), area2(7928), area3(7929); private static final Map<Integer, AreaCode> idMap = new HashMap(); static { for (AreaCode areaCode : AreaCode.values()) { idMap.put(areaCode.id, areaCode); } } private Integer id; private AreaCode(Integer id) { this.id = id; } public static AreaCode getById(Integer id) { return idMap.get(id); }