Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I eliminate duplicated Enum code?

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

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

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

like image 718
Dónal Avatar asked Sep 16 '08 21:09

Dónal


People also ask

Can enum have duplicate values?

CA1069: Enums should not have duplicate values.

Can we override enum?

By default, the enum value is its method name. You can however override it, for example if you want to store enums as integers in a database, instead of using their method name. An enum value doesn't have to be a string, as you can see in the example.

Can enum extend another Yes No?

Enum cannot extend any class in java,the reason is, by default Enum extends abstract base class java. lang. Enum. Since java does not support multiple inheritance for classes, Enum can not extend another class.


3 Answers

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());     }  } 
like image 51
dave Avatar answered Nov 06 '22 23:11

dave


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

like image 22
Alex Miller Avatar answered Nov 06 '22 23:11

Alex Miller


To tidy up dave's code:

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

Or more efficiently:

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));
    } 
}
like image 37
Tom Hawtin - tackline Avatar answered Nov 07 '22 00:11

Tom Hawtin - tackline