eliminating duplicate Enum code

2019-02-01 08:07发布

I have a large number of Enums that implement this interface:

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

A typical example is:

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;
            }
        }
    }
}

As you can imagine these methods are virtually identical in all implementations of CodableEnum. I would like to eliminate this duplication, but frankly don't know how. I tried using a class such as the following:

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

But this turns out to be fairly useless because:

  1. An enum cannot extend a class
  2. Elements of an enum (SKYPE, GOOGLE_TALK, etc.) cannot extend a class
  3. I cannot provide a default implementation of getByCode(), because DefaultCodableEnum is not itself an Enum. I tried changing DefaultCodableEnum to extend java.lang.Enum, but this doesn't appear to be allowed.

Any suggestions that do not rely on reflection? Thanks, Don

15条回答
Ridiculous、
2楼-- · 2019-02-01 08:43

How about a static generic method? You could reuse it from within your enum's getByCode() methods or simply use it directly. I always user integer ids for my enums, so my getById() method only has do do this: return values()[id]. It's a lot faster and simpler.

查看更多
贪生不怕死
3楼-- · 2019-02-01 08:49

Here I have another solution:

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

The trick is that the inner class can be used to hold "global methods".

Worked pretty fine for me. OK, you have to implement 3 Methods, but those methods, are just delegators.

查看更多
【Aperson】
4楼-- · 2019-02-01 08:51

You could factor the duplicated code into a CodeableEnumHelper class:

public class CodeableEnumHelper {
    public static CodeableEnum getByCode(String code, CodeableEnum[] values) {
        for (CodeableEnum e : values) {
            if (e.getCode().equalsIgnoreCase(code)) {
                return e;
            }
        }
        return null;
    }
}

Each CodeableEnum class would still have to implement a getByCode method, but the actual implementation of the method has at least been centralized to a single place.

public enum IMType implements CodeableEnum {
    ...
    public IMType getByCode(String code) {
        return (IMType)CodeableEnumHelper.getByCode(code, this.values());
    } 
}
查看更多
聊天终结者
5楼-- · 2019-02-01 08:51

In my opinion, this would be the easiest way, without reflection and without adding any extra wrapper to your enum.

You create an interface that your enum implements:

public interface EnumWithId {

    public int getId();

}

Then in a helper class you just create a method like this one:

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;
}

This method could be then used like this:

MyUtil.getInstance().getById(MyEnum.class, myEnumId);
查看更多
看我几分像从前
6楼-- · 2019-02-01 08:53

About as close as I got to what you want was to create a template in IntelliJ that would 'implement' the generic code (using enum's valueOf(String name)). Not perfect but works quite well.

查看更多
老娘就宠你
7楼-- · 2019-02-01 08:54

Abstract enums are potentially very useful (and currently not allowed). But a proposal and prototype exists if you'd like to lobby someone in Sun to add it:

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

查看更多
登录 后发表回答