How to enable enum inheritance

I'm writing a library, which has a predefined set of values for an enum. Let say, my enum looks as below.

public enum EnumClass {
    FIRST("first"),
    SECOND("second"),
    THIRD("third");

    private String httpMethodType;

}

Now the client, who is using this library may need to add few more values. Let say, the client needs to add CUSTOM_FIRST and CUSTOM_SECOND. This is not overwriting any existing values, but makes the enum having 5 values.

After this, I should be able to use something like <? extends EnumClass> to have 5 constant possibilities.

What would be the best approach to achieve this?


You cannot have an enum extend another enum, and you cannot "add" values to an existing enum through inheritance.

However, enums can implement interfaces.

What I would do is have the original enum implement a marker interface (i.e. no method declarations), then your client could create their own enum implementing the same interface.

Then your enum values would be referred to by their common interface.

In order to strenghten the requirements, you could have your interface declare relevant methods, e.g. in your case, something in the lines of public String getHTTPMethodType();.

That would force implementing enums to provide an implementation for that method.

This setting coupled with adequate API documentation should help adding functionality in a relatively controlled way.

Self-contained example (don't mind the lazy names here)

package test;

import java.util.ArrayList;
import java.util.List;

public class Main {

    public static void main(String[] args) {
        List<HTTPMethodConvertible> blah = new ArrayList<>();
        blah.add(LibraryEnum.FIRST);
        blah.add(ClientEnum.BLABLABLA);
        for (HTTPMethodConvertible element: blah) {
            System.out.println(element.getHTTPMethodType());
        }
    }

    static interface HTTPMethodConvertible {
        public String getHTTPMethodType();
    }
    static enum LibraryEnum implements HTTPMethodConvertible {
        FIRST("first"),
        SECOND("second"),
        THIRD("third");
        String httpMethodType;
        LibraryEnum(String s) {
            httpMethodType = s;
        }
        public String getHTTPMethodType() {
            return httpMethodType;
        }
    }
    static enum ClientEnum implements HTTPMethodConvertible {
        FOO("GET"),BAR("PUT"),BLAH("OPTIONS"),MEH("DELETE"),BLABLABLA("POST");
        String httpMethodType;
        ClientEnum(String s){
            httpMethodType = s;
        }
        public String getHTTPMethodType() {
            return httpMethodType;
        }
    }
}

Output

first
POST

Enums are not extensible. To solve your problem simply

  • turn the enum in a class
  • create constants for the predefined types
  • if you want a replacement for Enum.valueOf: track all instances of the class in a static map

For example:

public class MyType {
    private static final HashMap<String,MyType> map = new HashMap<>();
    private String name;
    private String httpMethodType;

    // replacement for Enum.valueOf
    public static MyType valueOf(String name) {
         return map.get(name);
    }

    public MyType(String  name, String httpMethodType) {
         this.name = name;
         this.httpMethodType = httpMethodType;
         map.put(name, this);
    }

    // accessors
    public String name() { return name; }
    public String httpMethodType() { return httpMethodType; }

    // predefined constants
    public static final MyType FIRST = new MyType("FIRST", "first");
    public static final MyType SECOND = new MyType("SECOND", "second");
    ...
}

Think about Enum like a final class with static final instances of itself. Of course you cannot extend final class, but you can use non-final class with static final instances in your library. You can see example of this kind of definition in JDK. Class java.util.logging.Level can be extended with class containing additional set of logging levels.

If you accept this way of implementation, your library code example can be like:

public class EnumClass {
    public static final EnumClass FIRST = new EnumClass("first");
    public static final EnumClass SECOND = new EnumClass("second");
    public static final EnumClass THIRD = new EnumClass("third");

    private String httpMethodType;

    protected EnumClass(String name){
        this.httpMethodType = name;
    }
}

Client application can extend list of static members with inheritance:

public final class ClientEnum extends EnumClass{
    public static final ClientEnum CUSTOM_FIRST = new ClientEnum("custom_first");
    public static final ClientEnum CUSTOM_SECOND = new ClientEnum("custom_second");

    private ClientEnum(String name){
        super(name);    
    }
}

I think that this solution is close to what you have asked, because all static instances are visible from client class, and all of them will satisfy your generic wildcard.