Inner class for each member of enum?

Viewed 1204

Not sure if what I want is possible but I am trying to create an enum in which each member has its own inner class. These inner classes will all have the same name Context but will be implemented individually.

Ideally I would like them to be usable as such:

private handleType (MyEnum type) {
    switch (type) {

        case ENUM_VAL1:
            MyEnum.ENUM_VAL1.Context context = new MyEnum.ENUM_VAL1.Context();
            handleContext1(context);
            break;

        case ENUM_VAL2:
            MyEnum.ENUM_VAL2.Context context = new MyEnum.ENUM_VAL1.Context();
            handleContext2(context);
            break;

        case ENUM_VAL3:
            MyEnum.ENUM_VAL3.Context context = new MyEnum.ENUM_VAL1.Context();
            handleContext3(context);
            break;

        default:
            break;
}

Open to other way of implementing this. But basically I need a switchable enum where each member has a "value" (1,2,3...) and also a means of associating said enums with a unique class with constructor.

EDIT: Some background. This is to be used between two services who communicate via JSON http requests. The requests will contain some metadata, one field of which is an integer that maps to the enum. The context is a POJO, but is different for each ENUM_VALUE. Essentially, the context will be constructed and serialized into JSON. This json will effectively be just a String field called context within the top level json request. On the receiving service, there will be a switch on ENUM_VALUE, where the context is decoded appropriately and then dispatched to its appropriate handler.

EDIT2: This enum will be shared between the two services.

EDIT3: Here is a more explicit explanation of what I am attempting to do.

MyServiceRequest:

public class MyServiceRequest {
    String meta1;
    String meta2;
    int typeID;
    String context;
}

generating request:

MyServiceRequest req = new MyServiceRequest();
req.meta1 = ...
req.meta2 = ...
req.typeID = MyEnum.ENUM_VALUE.getCode(); // int

MyEnum.ENUM_VALUE.Context context = new MyEnum.ENUM_VALUE.Context(); // factory would be fine as well
... // populate context
req.context = toJSON(context);
requestJSON = toJSON(req);
post(requestJSON);

decoding request:

MyServiceRequest req = ...
MyEnum type = new MyEnum(req.typeID);
switch(type) {
    case ENUM_VALUE:
        MyEnum.ENUM_VALUE.Context context = fromJSON(req.context, MyEnum.ENUM_VALUE.Context.class);
        doSomething(context);
4 Answers

One think you could do instead is have your enum implement Supplier<Context>. Now each item would have to declare a get() method to create the individual Context sub type.

enum MyEnum implements Supplier<Context>{
   FOO{ @Override public Context get(){ return new FooContext(); } },
   BAR{ @Override public Context get(){ return new BarContext(); } }
}

which would make your client code much simpler:

private void handleType (MyEnum type) {
    handleContext(type.get());
}

Why use an inner class?

You could simply have a field context that gets initialized with different values for each enum constant. Like:

public enum Whatever {
  A(new AContext), B... 

  private final Context context;

  private Whatever(Context context) {
    this.context = context;
 .... 

I wouldn't recommend a separate inner class for every enum, just a separate implementation. Something like below would probably be your best approach, then you don't have to use a switch statment. Because you can just call getContext() on your type variable:

enum MyEnum{

    A(new Context(){
        // my implementation
    }),
    B(new Context(){
        // my other implementation
    }),
    ;

    private final Context context;

    MyEnum(Context context){
        this.context = context;
    }

    public Context getContext(){
        return context;
    }

    public interface Context{
        // do something
    }
}

The most significant problem with what you describe is that classes scoped to individual enum elements do not have names that are resolvable outside that element. That makes it impossible to instantiate such a class via the new operator outside the enum value, or to declare any method outside the enum value that has that class as argument or return type.

But you can largely work around that by declaring an interface type for the inner classes to implement, and providing a factory method to serve in place of a constructor for obtaining instances. For example:

enum MyEnum {
    ENUM_VAL1 {
        class Context implements MyEnum.Context {
            public void doSomething() {
                System.out.println(1);
            }
        }

        public MyEnum.Context createContext() {
            return new Context();
        }
    },
    ENUM_VAL2 {
        class Context implements MyEnum.Context {
            public void doSomething() {
                System.out.println(2);
            }
        }

        public MyEnum.Context createContext() {
            return new Context();
        }
    };

    interface Context {
        public void doSomething();
    }

    public abstract Context createContext();
}

public class EnumScope {

    private void handleContext1(MyEnum.Context context) {
        context.doSomething();
    }

    private void handleContext2(MyEnum.Context context) {
        context.doSomething();
    }

    private void handleType(MyEnum type) {
        MyEnum.Context context = type.createContext();

        switch (type) {
            case ENUM_VAL1:
                handleContext1(context);
                break;
            case ENUM_VAL2:
                handleContext2(context);
                break;
        }
    }
}

I think this is a bit dubious, however -- especially having methods specific to particular enum values that do not actually belong to those enum values. There is likely an altogether different approach that would serve you better, but you have described the problem too generically for us to suggest such an alternative.


Update

After considering your edits to the question and your subsequent comments, I am inclined to stick with my assessment that what you're proposing is a bit dubious.

Take a step back and consider the problem from a wider perspective. You are generating, serializing (to JSON), deserializing, and consuming requests of several types (distinguished, at present, by an ID code that appears within). It makes sense to represent each type of request with a class bearing the appropriate properties, including those of the varying context data of each type. If there are some intentional commonalities, then perhaps these should implement a common interface that describes them, or even extend a common base class.

With that done, the JSON serialization / deserialization is a solved (more than once) problem. Unless you like reinventing the wheel, I'm inclined to suggest Google GSON for this. I need to qualify that with an admission that I haven't much personal experience with GSON, but it's quite popular, and you'll see a lot of questions (and answers) about it here. You'll also find some good online tutorials.

Related