消除重复的Enum代码
我有很多实现此接口的枚举:
/** * Interface for an enumeration, each element of which can be uniquely identified by it's code */ public interface CodableEnum { /** * Get the element with a particular code * @param code * @return */ public CodableEnum getByCode(String code); /** * Get the code that identifies an element of the enum * @return */ public String getCode(); }
一个典型的例子是:
public enum IMType implements CodableEnum { MSN_MESSENGER("msn_messenger"), GOOGLE_TALK("google_talk"), SKYPE("skype"), YAHOO_MESSENGER("yahoo_messenger"); private final String code; IMType (String code) { this.code = code; } public String getCode() { return code; } public IMType getByCode(String code) { for (IMType e : IMType.values()) { if (e.getCode().equalsIgnoreCase(code)) { return e; } } } }
我们可以想象这些方法在CodableEnum的所有实现中实际上是相同的。我想消除这种重复,但坦率地说不知道如何做。我尝试使用如下类:
public abstract class DefaultCodableEnum implements CodableEnum { private final String code; DefaultCodableEnum(String code) { this.code = code; } public String getCode() { return this.code; } public abstract CodableEnum getByCode(String code); }
但这实际上是没有用的,因为:
- 枚举不能扩展类
- 枚举的元素(SKYPE,GOOGLE_TALK等)无法扩展类
- 我无法提供getByCode()的默认实现,因为DefaultCodableEnum本身不是枚举。我尝试更改DefaultCodableEnum以扩展java.lang.Enum,但是似乎不允许这样做。
有没有不依靠反思的建议?
谢谢,
大学教师
解决方案
回答
不幸的是,我认为没有办法做到这一点。最好的选择是完全放弃Emum,并使用常规的类扩展和静态成员。否则,请习惯于复制该代码。对不起。
回答
看来我们实际上是在实现运行时类型信息。 Java将其作为语言功能提供。
我建议我们查找RTTI或者反射。
回答
我认为这是不可能的。但是,如果要使用枚举值的名称作为代码,则可以使用枚举的valueOf(String name)方法。
回答
静态泛型方法怎么样?我们可以从枚举的getByCode()方法中重用它,也可以直接使用它。我总是为枚举使用整数ID,所以我的getById()方法只能这样做:return values()[id]。它更快,更简单。
回答
抽象枚举可能非常有用(当前不允许使用)。但是,如果我们想在Sun中游说某人添加提案和原型,则可以使用该提案和原型:
http://freddy33.blogspot.com/2007/11/abstract-enum-ricky-carlson-way.html
Sun RFE:
http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6570766
回答
我编写的本地化组件也有类似的问题。我的组件旨在访问带有索引到资源束的枚举常量的本地化消息,这不是一个难题。
我发现我到处都复制并粘贴了相同的"模板"枚举代码。我避免重复的解决方案是代码生成器,该代码生成器接受带有枚举常量名称和构造函数args的XML配置文件。输出是具有"重复"行为的Java源代码。
现在,我维护配置文件和生成器,而不是所有重复的代码。我到处都有枚举源代码的任何地方,现在都有一个XML配置文件。我的构建脚本检测过期的生成文件,并调用代码生成器来创建枚举代码。
HTH,
格雷格
回答
如果我们真的想要继承,请不要忘记可以自己实现枚举模式,就像在糟糕的旧Java 1.4天内一样。
回答
与我想要的差不多,是在IntelliJ中创建一个模板来"实现"通用代码(使用枚举的valueOf(String name))。不完美,但效果很好。
回答
我们可以将重复的代码分解为CodeableEnumHelper
类:
public class CodeableEnumHelper { public static CodeableEnum getByCode(String code, CodeableEnum[] values) { for (CodeableEnum e : values) { if (e.getCode().equalsIgnoreCase(code)) { return e; } } return null; } }
每个" CodeableEnum"类仍必须实现" getByCode"方法,但是该方法的实际实现至少已集中到一个地方。
public enum IMType implements CodeableEnum { ... public IMType getByCode(String code) { return (IMType)CodeableEnumHelper.getByCode(code, this.values()); } }
回答
创建一个类型安全的实用程序类,该类将通过代码加载枚举:
该界面归结为:
public interface CodeableEnum { String getCode(); }
实用程序类为:
import java.lang.reflect.InvocationTargetException; public class CodeableEnumUtils { @SuppressWarnings("unchecked") public static <T extends CodeableEnum> T getByCode(String code, Class<T> enumClass) throws IllegalArgumentException, SecurityException, IllegalAccessException, InvocationTargetException, NoSuchMethodException { T[] allValues = (T[]) enumClass.getMethod("values", new Class[0]).invoke(null, new Object[0]); for (T value : allValues) { if (value.getCode().equals(code)) { return value; } } return null; }
}
演示用法的测试用例:
import junit.framework.TestCase; public class CodeableEnumUtilsTest extends TestCase { public void testWorks() throws Exception { assertEquals(A.ONE, CodeableEnumUtils.getByCode("one", A.class)); assertEquals(null, CodeableEnumUtils.getByCode("blah", A.class)); } enum A implements CodeableEnum { ONE("one"), TWO("two"), THREE("three"); private String code; private A(String code) { this.code = code; } public String getCode() { return code; } } }
现在,我们只需复制getCode()方法,并且getByCode()方法放在一个位置。也可以将所有异常都包装在一个RuntimeException中:)
回答
整理dave的代码:
public class CodeableEnumHelper { public static <E extends CodeableEnum> E getByCode( String code, E[] values ) { for (E e : values) { if (e.getCode().equalsIgnoreCase(code)) { return e; } } return null; } } public enum IMType implements CodableEnum { ... public IMType getByCode(String code) { return CodeableEnumHelper.getByCode(code, values()); } }
或者更有效地:
public class CodeableEnumHelper { public static <E extends CodeableEnum> Map<String,E> mapByCode( E[] values ) { Map<String,E> map = new HashMap<String,E>(); for (E e : values) { map.put(e.getCode().toLowerCase(Locale.ROOT), value) { } return map; } } public enum IMType implements CodableEnum { ... private static final Map<String,IMType> byCode = CodeableEnumHelper.mapByCode(values()); public IMType getByCode(String code) { return byCode.get(code.toLowerCase(Locale.ROOT)); } }
回答
在特定情况下,getCode()/ getByCode(String code)方法似乎与所有枚举提供的toString()/ valueOf(String value)方法的行为非常接近。我们为什么不想使用它们?
回答
另一个解决方案是不将任何内容放入枚举本身,而只为每个枚举提供双向映射Enum <-> Code。我们可以例如为此,请使用Google收藏夹中的ImmutableBiMap。
这样,根本没有重复的代码。
例子:
public enum MYENUM{ VAL1,VAL2,VAL3; } /** Map MYENUM to its ID */ public static final ImmutableBiMap<MYENUM, Integer> MYENUM_TO_ID = new ImmutableBiMap.Builder<MYENUM, Integer>(). put(MYENUM.VAL1, 1). put(MYENUM.VAL2, 2). put(MYENUM.VAL3, 3). build();
回答
我认为,这是最简单的方法,无需进行反思,也无需在枚举中添加任何额外的包装。
我们创建一个由枚举实现的接口:
public interface EnumWithId { public int getId(); }
然后在一个帮助器类中,我们只需创建一个像这样的方法:
public <T extends EnumWithId> T getById(Class<T> enumClass, int id) { T[] values = enumClass.getEnumConstants(); if (values != null) { for (T enumConst : values) { if (enumConst.getId() == id) { return enumConst; } } } return null; }
然后可以这样使用该方法:
MyUtil.getInstance().getById(MyEnum.class, myEnumId);