消除重复的枚举代码

我有大量实现此接口的Enums:

/** * 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); } 

但事实certificate这是相当无用的,因为:

  1. 枚举不能扩展类
  2. 枚举元素(SKYPE,GOOGLE_TALK等)不能扩展类
  3. 我无法提供getByCode()的默认实现,因为DefaultCodableEnum本身不是Enum。 我尝试更改DefaultCodableEnum以扩展java.lang.Enum,但似乎不允许这样做。

任何不依赖反思的建议? 谢谢,唐

您可以将重复的代码分解为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()); } } 

抽象枚举可能非常有用(目前不允许)。 但是,如果您想在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

整理戴夫的代码:

 public class CodeableEnumHelper { public static  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  Map mapByCode( E[] values ) { Map map = new HashMap(); for (E e : values) { map.put(e.getCode().toLowerCase(Locale.ROOT), value) { } return map; } } public enum IMType implements CodableEnum { ... private static final Map byCode = CodeableEnumHelper.mapByCode(values()); public IMType getByCode(String code) { return byCode.get(code.toLowerCase(Locale.ROOT)); } } 

我写了一个与本地化组件类似的问题。 我的组件旨在访问具有索引到资源包的枚举常量的本地化消息,而不是硬问题。

我发现我正在复制并粘贴相同的“模板”枚举代码。 我避免重复的解决方案是一个代码生成器,它接受带有枚举常量名称和构造函数args的XML配置文件。 输出是具有“重复”行为的Java源代码。

现在,我维护配置文件和生成器,而不是所有重复的代码。 我到处都有枚举源代码,现在有一个XML配置文件。 我的构建脚本检测过时的生成文件,并调用代码生成器来创建枚举代码。

你可以在这里看到这个组件。 我正在复制和粘贴的模板被分解为XSLT样式表 。 代码生成器运行样式表转换。 与生成的枚举源代码相比, 输入文件非常简洁。

HTH,
格雷格

不幸的是,我认为没有办法做到这一点。 你最好的选择是完全放弃emums并使用传统的类扩展和静态成员。 否则,习惯于复制该代码。 抱歉。

创建一个类型安全的实用程序类,它将按代码加载枚举:

界面归结为:

 public interface CodeableEnum { String getCode(); } 

实用程序类是:

 import java.lang.reflect.InvocationTargetException; public class CodeableEnumUtils { @SuppressWarnings("unchecked") public static  T getByCode(String code, Class 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()方法位于一个位置。 将所有exception包装在单个RuntimeException中可能会很好:)

我在这里有另一个解决方案

 interface EnumTypeIF { String getValue(); EnumTypeIF fromValue(final String theValue); EnumTypeIF[] getValues(); class FromValue { private FromValue() { } public static EnumTypeIF valueOf(final String theValue, EnumTypeIF theEnumClass) { for (EnumTypeIF c : theEnumClass.getValues()) { if (c.getValue().equals(theValue)) { return c; } } throw new IllegalArgumentException(theValue); } } 

诀窍是内部类可用于保存“全局方法”。

对我来说工作得很好。 好的,你必须实现3个方法,但这些方法只是委托人。

看起来您实际上正在实现运行时类型信息。 Java将此作为语言function提供。

我建议你查看RTTI或反思。

我不认为这是可能的。 但是,如果要使用枚举值的名称作为代码,则可以使用enum的valueOf(String name)方法。

静态generics方法怎么样? 您可以在枚举的getByCode()方法中重复使用它,或者直接使用它。 我总是为我的枚举使用整数id,所以我的getById()方法只做这样做:return values()[id]。 它更快更简单。

如果您真的想要inheritance,请不要忘记您可以自己实现枚举模式 ,就像在糟糕的旧Java 1.4天中一样。

尽可能接近你想要的是在IntelliJ中创建一个“实现”通用代码的模板(使用enum的valueOf(String name))。 不完美,但效果很好。

在您的特定情况下,getCode()/ getByCode(String code)方法似乎对所有枚举提供的toString()/ valueOf(String value)方法的行为非常封闭(委婉地说)。 你为什么不想用它们?

另一种解决方案是不将任何内容放入枚举本身,只为每个枚举提供双向映射Enum < - >代码。 例如,您可以使用Google Collections中的ImmutableBiMap 。

这样就没有重复的代码了。

例:

 public enum MYENUM{ VAL1,VAL2,VAL3; } /** Map MYENUM to its ID */ public static final ImmutableBiMap MYENUM_TO_ID = new ImmutableBiMap.Builder(). put(MYENUM.VAL1, 1). put(MYENUM.VAL2, 2). put(MYENUM.VAL3, 3). build(); 

在我看来,这将是最简单的方法,没有reflection,也没有为你的枚举添加任何额外的包装。

您创建了枚举实现的接口:

 public interface EnumWithId { public int getId(); } 

然后在一个帮助器类中,您只需创建一个类似这样的方法:

 public  T getById(Class 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);