Java 如何通过属性获取枚举类型?
我已经编写了一个enum类,我想按类型获取属性,或者按属性获取类型,但这似乎是不可能的Java 如何通过属性获取枚举类型?,java,enums,Java,Enums,我已经编写了一个enum类,我想按类型获取属性,或者按属性获取类型,但这似乎是不可能的 public enum AreaCode { area1(7927), area2(7928), area3(7929); private final int ac; AreaCode(int ac) { this.ac = ac; } int areaCode(){ return ac; } Ar
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;
}
}
}
上面的代码将无法编译。如何使
区域(int n)
工作?您需要做的就是添加一个默认案例,以便该方法始终返回某些内容或引发异常:
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));
}
}
该方法应该是静态的,并且在任何情况下都应该返回一些内容。让它在默认情况下返回null,或者让它抛出一个IllegalArgumentException(或其他一些异常):这取决于您
注意:阅读编译器错误消息应该可以帮助您。它不编译的原因是缺少返回语句。您只返回已识别案例的内容。我建议您添加一个默认案例,该案例返回一些指示区号未知的内容。名为unknown或null的枚举常量可以完成此任务。您应该在switch语句中包含一个default子句,因为编译器不知道当n不是7927、7928或7929时该怎么做。除了其他海报指出的问题,我重写了方法以避免重复信息(!):
我建议添加静态映射,将整数映射到区号,然后使用此映射
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);
}
}
}
公共枚举区域代码{
第1区(7927)、第2区(7928)、第3区(7929);
私人终审法院;
私有静态映射id2code=newhashmap();
区域代码(int ac){
this.ac=ac;
id2code.put(ac,this);
}
国际区号(){
返回ac;
}
区域代码区域(int n){
返回id2code.get(n);
}
}
}
您可以使用下一个构造
public enum AreaCode {
area1(7927),
area2(7928),
area3(7929);
private static final Map<Integer, AreaCode> idMap = new HashMap<Integer, 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);
}
}
公共枚举区域代码{
第1区(7927),
第2区(7928),
第3区(7929);
私有静态最终映射idMap=newhashmap();
静止的{
对于(区号区号:AreaCode.values()){
idMap.put(区号.id,区号);
}
}
私有整数id;
专用区号(整数id){
this.id=id;
}
公共静态区域代码getById(整数id){
返回idMap.get(id);
}
}
正如人们所说,剥猫皮的方法不止一种。首先,枚举值应该是大写的(用下划线分隔的单词),因为它们是常量值,并且应该按照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)(恒定时间),但您需要对每种情况进行硬编码(不是很动态)
环 查找是O(n)(线性时间),所以您需要在每个值上循环,直到找到匹配项,但您确实需要对每个情况进行硬编码(动态)
查找 查找是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;
导入java.util.HashMap;
导入java.util.Map;
公共枚举区域代码{
第1区(7927),
第2区(7928),
第三区(7929);;
私有静态最终地图查找\u地图;
私人国际区号;
静止的{
LOOKUP_MAP=新HashMap();
对于(区号区号:AreaCode.values()){
查找映射放置(areaCode.getAreaCode(),areaCode);
}
LOOKUP\u MAP=集合。不可修改映射(LOOKUP\u MAP);
}
专用区号(国际区号){
this.areaCode=区域代码;
}
public int getAreaCode(){
返回区号;
}
公共静态区号按区号检索(int n){
返回查找映射get(n);
}
@凌驾
公共字符串toString(){
返回String.format(“%s[name=\%s\”,value=\%d\”,
this.getClass().getName()、this.name()、this.getAreaCode());
}
}
一般方法
EnumUtils.java
import java.util.Collections;
导入java.util.HashMap;
导入java.util.Map;
公共类枚举{
公共静态接口枚举属性{
U值(T型);
}
公共静态映射createLookup(类enumTypeClass,EnumProperty属性){
映射查找=新的HashMap();
对于(T类型:enumTypeClass.getEnumConstants()){
lookup.put(prop.getValue(type),type);
}
返回集合。不可修改映射(查找);
}
}
import java.util.Map;
公共枚举区域代码{
第1区(7927),
第2区(7928),
第三区(7929);;
私有静态最终EnumUtils.EnumProperty ENUM_PROP;
私有静态最终地图查找\u地图;
静止的{
ENUM_PROP=新的EnumUtils.EnumProperty(){
@凌驾
公共整数getValue(区域代码){
返回代码。getAreaCode();
}
};
LOOKUP\u MAP=EnumUtils.createLookup(AreaCode.class,ENUM\u PROP);
}
私人国际区号;
专用区号(国际区号){
this.areaCode=区域代码;
}
public int getAreaCode(){
返回区号;
}
公共静态区号按区号检索(int n){
返回查找映射get(n);
}
@凌驾
公共字符串toString(){
返回String.format(“%s[name=\%s\”,value=\%d\”,
this.getClass().getName()、this.name()、this.getAreaCode());
}
}
我编写了一个帮助程序,以避免污染枚举代码,并允许您按属性获取任何类型的枚举。您将不再需要在每个枚举类型上声明特定的方法
就你而言,你
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());
}
}
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());
}
}
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());
}
}
// 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;
}
}
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
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");
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");
}
}
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;
}
}
public static Optional<AreaCode> getAreaCode(int area_code){
return Arrays.stream(AreaCode.values()).filter(e -> e.ac == area_code).findFirst();
}