23

我有大量实现此接口的枚举:

/**
 * Interface for an enumeration, each element of which can be uniquely identified by its 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);  
}

但事实证明这毫无用处,因为:

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

有什么不依赖反思的建议吗?谢谢,唐

4

15 回答 15

13

您可以将重复的代码分解为一个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());
    } 
}
于 2008-09-16T23:22:40.837 回答
7

抽象枚举可能非常有用(目前不允许)。但是,如果您想游说 Sun 中的某个人添加它,那么存在一个提案和原型:

http://freddy33.blogspot.com/2007/11/abstract-enum-ricky-carlson-way.html

太阳 RFE:

http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6570766

于 2008-09-16T21:43:23.210 回答
5

整理 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));
    } 
}
于 2008-09-17T10:41:26.227 回答
2

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

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

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

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

HTH,
格雷格

于 2008-09-16T21:45:25.220 回答
1

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

于 2008-09-16T21:24:58.060 回答
1

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

界面归结为:

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 中可能会很好:)

于 2008-09-17T00:02:55.980 回答
1

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

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 个方法,但这些方法只是委托者。

于 2011-03-20T11:02:39.660 回答
0

看起来您实际上是在实现运行时类型信息。Java 将此作为一种语言特性提供。

我建议你查一下 RTTI 或反射。

于 2008-09-16T21:28:28.863 回答
0

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

于 2008-09-16T21:33:20.707 回答
0

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

于 2008-09-16T21:37:06.610 回答
0

如果你真的想要继承,不要忘记你可以自己实现枚举模式,就像在糟糕的 Java 1.4 时代一样。

于 2008-09-16T21:53:48.210 回答
0

与您想要的差不多,就是在 IntelliJ 中创建一个模板,该模板将“实现”通用代码(使用枚举的 valueOf(String name))。不完美,但效果很好。

于 2008-09-16T22:39:10.363 回答
0

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

于 2008-09-17T11:00:54.277 回答
0

另一种解决方案是不将任何内容放入枚举本身,而只为每个枚举提供一个双向映射 Enum <-> 代码。例如,您可以为此使用Google Collections 中的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();
于 2010-03-18T17:06:14.580 回答
0

在我看来,这将是最简单的方法,无需反射,也无需为您的枚举添加任何额外的包装器。

您创建一个枚举实现的接口:

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);
于 2010-05-19T15:42:13.547 回答