我编写了一个枚举类,我希望按类型获取属性并获取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)
工作?
答案 0 :(得分:28)
除了其他海报所指出的问题外,我会重写方法以避免重复信息(keep it DRY!):
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));
}
答案 1 :(得分:14)
正如他们所说,给猫皮肤化的方法不止一种。首先,枚举值应该是大写的(由下划线分隔的单词),因为它们是常量值,并且应该通过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<Integer, AreaCode> LOOKUP_MAP;
private int areaCode;
static {
LOOKUP_MAP = new HashMap<Integer, AreaCode>();
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());
}
}
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
public class EnumUtils {
public static interface EnumProperty<T extends Enum<T>, U> {
U getValue(T type);
}
public static <T extends Enum<T>, U> Map<U, T> createLookup(Class<T> enumTypeClass, EnumProperty<T, U> prop) {
Map<U, T> lookup = new HashMap<U, T>();
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<AreaCode, Integer> ENUM_PROP;
private static final Map<Integer, AreaCode> LOOKUP_MAP;
static {
ENUM_PROP = new EnumUtils.EnumProperty<AreaCode, Integer>() {
@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());
}
}
答案 2 :(得分:11)
您需要做的就是添加一个默认大小写,以便该方法始终返回一些内容或抛出异常:
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));
}
}
答案 3 :(得分:4)
我建议添加将整数映射到区域代码的静态地图,然后使用此地图。
public enum AreaCode {
area1(7927), area2(7928), area3(7929);
private final int ac;
private static Map<Integer, AreaCode> id2code = new HashMap<Integer, AreaCode>();
AreaCode(int ac) {
this.ac = ac;
id2code.put(ac, this);
}
int areaCode(){
return ac;
}
AreaCode area(int n){
return id2code.get(n);
}
}
}
答案 4 :(得分:2)
它不编译的原因是缺少return语句。您只返回被识别的案例。我建议你添加一个默认情况,返回一些表明区号不知道的情况。名称为unknown
或null的枚举常量可以完成这项工作。
答案 5 :(得分:1)
该方法应该是静态的,并且应该在每种情况下返回一些内容。让它在默认情况下返回null,或者使它抛出IllegalArgumentException(或其他一些异常):这取决于你。
注意:阅读编译器错误消息应该为您提供指导。
答案 6 :(得分:1)
我编写了一个帮助程序,以避免污染枚举代码,并允许您按属性获取任何类型的枚举。您将不再需要在每个枚举类型上声明特定方法。
在您的情况下,您可以像下面一样使用它(您的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 <code>E</code> associated to the attribute
* @param enumType
* @param method
* @param expectedValue
* @return
*/
public static <E extends Enum<E>, R> E getEnum(final Class<E> enumType, final Function<E, R> 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 <E extends Enum<E>, R> E getEnum(final Class<E> enumType, final Function<E, R> 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:使用反射API
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 <code>E</code> which first attribute has value {@link obj}.
* @param enumType
* @param value
* @return
*/
public static <E extends Enum<E>> E getEnum(final Class<E> enumType, final Object value) {
return getEnum(enumType, null, value);
}
/**
* Get the enum of type <code>E</code> which attribute {@link attributeName} has value {@link obj}.
* @param enumType
* @param value
* @return
*/
public static <E extends Enum<E>> E getEnum(final Class<E> enumType, final String attributeName, final Object value) {
return getEnum(enumType, ElementType.FIELD, attributeName, value);
}
/**
* Get the enum of type <code>E</code> associated to the attribute
* @param enumType
* @param elementType
* @param name
* @param value
* @return
*/
public static <E extends Enum<E>> E getEnum(final Class<E> 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
您还可以指定要使用的属性(默认情况下,它是枚举的第一个属性)
// 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))中声明的静态哈希映射。 否则,因为它使用反射API(java 6)或Functional(java 8),所以性能比标准代码片段慢。 从规模来看,更昂贵。
但是可以添加缓存系统(EhCache,map ..)。
<强>安全性:强> 如果使用错误的参数调用它,这个帮助程序可以在运行时抛出异常,而标准代码在编译期间会产生错误。
反射API 慢 ,因此它不适合生产! 请勿在有时间限制的生产环境中使用它。
只需添加一个静态方法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<nbrIterations; ++i) {
Move.valueOf("FORWARD");
Move.valueOf("RIGHT");
Move.valueOf("LEFT");
}
System.out.println("Trivial: " + (System.currentTimeMillis() - start) + "ms");
}
private static void doRegular(final int nbrIterations) {
long start = System.currentTimeMillis();
for (int i=0; i<nbrIterations; ++i) {
Move.getMove("F");
Move.getMove("R");
Move.getMove("L");
}
System.out.println("Regular: " + (System.currentTimeMillis() - start) + "ms");
}
private static void doFunctional(final int nbrIterations) {
long start = System.currentTimeMillis();
for (int i=0; i<nbrIterations; ++i) {
FunctionalEnumHelper.getEnum(Move.class, Move::getLabel, "F");
FunctionalEnumHelper.getEnum(Move.class, Move::getLabel, "R");
FunctionalEnumHelper.getEnum(Move.class, Move::getLabel, "L");
}
System.out.println("Functional: " + (System.currentTimeMillis() - start) + "ms");
}
private static void doReflection(final int nbrIterations) {
long start = System.currentTimeMillis();
for (int i=0; i<nbrIterations; ++i) {
EnumHelper.getEnum(Move.class, "F");
EnumHelper.getEnum(Move.class, "R");
EnumHelper.getEnum(Move.class, "L");
}
System.out.println("Reflection (argument): " + (System.currentTimeMillis() - start) + "ms");
long start2 = System.currentTimeMillis();
for (int i=0; i<nbrIterations; ++i) {
EnumHelper.getEnum(Move.class, ElementType.METHOD, "getLabel", "F");
EnumHelper.getEnum(Move.class, ElementType.METHOD, "getLabel", "R");
EnumHelper.getEnum(Move.class, ElementType.METHOD, "getLabel", "L");
}
System.out.println("Reflection (method): " + (System.currentTimeMillis() - start2) + "ms");
}
}
结果如下: 琐事:28ms |常规:53ms |功能:171ms |反思(论点):890ms |反思(方法):800ms
这个基准测试显示功能解决方案比常规解决方案贵一点(在枚举中有丑陋的代码......),但它仍然可以接受。具有反射的解决方案很美观,但它不适合有时间限制的环境。
答案 7 :(得分:1)
这是另一种方法(使用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<Integer, AreaCode> 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;
}
}
答案 8 :(得分:0)
你应该在你的switch语句中包含一个default子句,因为当n不是7927,7928或7929时,编译器不会找不到该做什么。
我希望它有所帮助。
答案 9 :(得分:0)
您可以使用下一个构造
public enum AreaCode {
area1(7927), area2(7928), area3(7929);
private static final Map<Integer, AreaCode> idMap = new HashMap<AreaCode>();
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);
}
答案 10 :(得分:0)
这样,您可以在方法签名上清楚地表明您可能会找到枚举。
public static Optional<AreaCode> getAreaCode(int area_code){
return Arrays.stream(AreaCode.values()).filter(e -> e.ac == area_code).findFirst();
}
答案 11 :(得分:-1)
public static AreaCode retrieveByAreaCode(int n) {
switch (n) {
case AreaCode.AREA_1.getAreaCode():
return AreaCode.AREA_1;
case AreaCode.AREA_2.getAreaCode():
return AreaCode.AREA_2;
case AreaCode.AREA_3.getAreaCode():
return AreaCode.AREA_3;
default:
return null;
}
}