How to enable enum inheritance

JavaInheritanceEnums

Java Problem Overview


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?

Java Solutions


Solution 1 - Java

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

Solution 2 - Java

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

Solution 3 - Java

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.

Solution 4 - Java

We Fixed enum inheritance issue this way, hope it helps

Our App has few classes and each has few child views(nested views), in order to be able to navigate between childViews and save the currentChildview we saved them as enum inside each Class. but we had to copy paste, some common functionality like next, previous and etc inside each enum. To avoid that we needed a BaseEnum, we used interface as our base enum:

public interface IBaseEnum {
    IBaseEnum[] getList();
    int getIndex();

    class Utils{
        public IBaseEnum next(IBaseEnum enumItem, boolean isCycling){
            int index = enumItem.getIndex();
            IBaseEnum[] list = enumItem.getList();
            if (index + 1 < list.length) {
                return list[index + 1];
            } else if(isCycling)
                return list[0];
            else
                return null;
        }

        public IBaseEnum previous(IBaseEnum enumItem, boolean isCycling) {
            int index = enumItem.getIndex();
            IBaseEnum[] list = enumItem.getList();

            IBaseEnum previous;
            if (index - 1 >= 0) {
                previous = list[index - 1];
            }
            else {
                if (isCycling)
                    previous = list[list.length - 1];
                else
                    previous = null;
            }
            return previous;
        }
    }
}

and this is how we used it

enum ColorEnum implements IBaseEnum {
    RED,
    YELLOW,
    BLUE;
    @Override
    public IBaseEnum[] getList() {
        return values();
    }

    @Override
    public int getIndex() {
        return ordinal();
    }

    public ColorEnum getNext(){
    return (ColorEnum) new Utils().next(this,false);
    }

    public ColorEnum getPrevious(){
        return (ColorEnum) new Utils().previous(this,false);
    }
}

you could add getNext /getPrevious to the interface too

Solution 5 - Java

@wero's answer is very good but has some problems:
the new MyType("FIRST", "first"); will be called before map = new HashMap<>();. in other words, the map will be null when map.add() is called. unfortunately, the occurring error will be NoClassDefFound and it doesn't help to find the problem. check this:

public class Subject {

    // predefined constants
    public static final Subject FIRST;
    public static final Subject SECOND;
    private static final HashMap<String, Subject> map;

    static {
        map = new HashMap<>();

        FIRST = new Subject("FIRST");
        SECOND = new Subject("SECOND");
    }

    private final String name;

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

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

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

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionKiran A BView Question on Stackoverflow
Solution 1 - JavaMenaView Answer on Stackoverflow
Solution 2 - JavaweroView Answer on Stackoverflow
Solution 3 - JavaFertonder BrateView Answer on Stackoverflow
Solution 4 - JavaAnnieView Answer on Stackoverflow
Solution 5 - JavaNegar ZamiriView Answer on Stackoverflow