user3590149
user3590149

Reputation: 1605

Java - SubType Enums or SubClass

I'm trying to have an enum correspond to a class or return that class but I get to resolve. Is this behavior possible if I use the CommandType.SELLSHARES for it to return SellShares.class? Or can I organize the enums with different categories that inherit from the parent type?

Commands - ADMIN, USER, CLIENT

public enum CommandType {

// SELLSHARES, BUYSHARES, UPDATEUSER, ADDUSER, ADMINASSIGNMENT, BANUSER, CHANGESTATUS, REMOVEUSER

    SELLSHARES (SellShares.class),
    BUYSHARES (BuyShares.class);

    private Class<Command> command;

    CommandType(Class<Command> command) {
        this.command = command;
    }

    private Class<Command> command() { return command; }

    public static <T extends Enum<T>> T getInstance(final String value,
            final Class<T> enumClass) {
        return Enum.valueOf(enumClass, value);
    }

}

OR

public enum CommandType {
    AdminCommands,
    UserCommands
}

enum AdminCommands {
    UPDATEUSER,
    ADDUSER,
    ADMINASSIGNMENT,
    BANUSER,
    CHANGESTATUS,
    REMOVEUSER
}

enum User {
    SELLSHARES,
    BUYSHARES
}

Having problem with getByType

void initialCommands() throws Exception
    {
        listCommands = Commands.getByType(Commands.Type.ADMIN);

        for (Commands command : listCommands)
            {
                Command commandss = command.newInstance();
                //addCommand(command.getCommand());
                //log.trace(command.newInstance());

            }

    }

Upvotes: 3

Views: 5121

Answers (2)

Blessed Geek
Blessed Geek

Reputation: 21664

I wish to offer this advice and pattern.

  • Do not ever use for/while/loops on an enum.
  • Reduce use of if-then-else on an enum.
  • But exploit the fact that an enum is an efficient hashmap. Build actions into the enum so that you don't have to iterate on the enum to resolve actions due to an enum.
  • Avoid using if blocks to resolve the appropriate action for an enum, but build the function lambda into the enum.
  • Design your code to use EnumSet as much as possible to "sub class" your enum.

Redesign the flow of your code to make use of EnumSet. Don't use the plural but Command as the enum class name. So that you'd reference a command holder as Command.ADDUSER rather than Commands.ADDUSER.

Example of using EnumSet to categorize items in the enum,

enum Command {
    UPDATEUSER,
    ADDUSER,
    ADMINASSIGNMENT,
    BANUSER,
    CHANGESTATUS,
    REMOVEUSER,
    SELLSHARES,
    BUYSHARES,
    ;

    final static public EnumSet<AdminCommands> ADMIN = EnumSet.of(
      UPDATEUSER,
      ADDUSER,
      ADMINASSIGNMENT,
      BANUSER,
      CHANGESTATUS,
      REMOVEUSER
    );

    final static public EnumSet<AdminCommands> USER= EnumSet.of(
      SELLSHARES,
      BUYSHARES
    );
}

Building the class constructor call, and their respective actions into the enum:

interface Commander{}
@FunctionalInterface
public interface CommanderNoArg extends Commander {
  Action getAction();
}
@FunctionalInterface
public interface Commander1Arg extends Commander {
  Action getAction(ActionContext ctx);
}
@FunctionalInterface
public interface Commander2Arg extends Commander {
  Action getAction(ActionContext ctx, Options opts);
}

// Auxiliary casters needed due to Java's silly deficient treatment of generics
static private CommanderNoArg hasNoArg(CommanderNoArg lambd) {
  return lambd;
}
static private Commander1Arg has1Arg(Commander1Arg lambd) {
  return lambd;
}
static private Commander2Arg has2Args(Commander2Arg lambd) {
  return lambd;
}


enum Command {
    UPDATEUSER(hasNoArg(UserContext::new) ),
    ADDUSER(hasNoArg(UserContext::new) ),
    ADMINASSIGNMENT(has1Arg(AdminContext::new) ),
    BANUSER(hasNoArg(UserContext::new) ),
    CHANGESTATUS(hasNoNoArg(UserContext::new) ),
    REMOVEUSER(hasNoNoArg(UserContext::new) ),
    SELLSHARES(has2Args(UserContext::new) ),
    BUYSHARES(has2Args(UserContext::new) ),
    ;

    final public Commander commander;
    private Command(Commander cmdr) {
      this.commander = cmdr;
    }

}

Where because

  • UserContext has constructor that requires no argument
  • UserContext also has constructor that requires 2 arguments
  • AdminContext has constructor that requires 1 argument.

You would get the Action class by, e.g.,

// AdminContext requires 1 argument
Assignment assg = getAssgFromSomewhere();
Action act =
  ((Commander1Arg )Command.ADMINASSIGNMENT.commander)
     .getAction(assg);

Or perhaps, you could feed in lambdas of static methods into the enum items rather than a class.

Upvotes: 0

sp00m
sp00m

Reputation: 48827

You must be looking for something like this:

public enum Commands {

    UPDATE_USER(Type.ADMIN, UpdateUser.class),
    ADD_USER(Type.ADMIN, AddUser.class),
    ADMIN_ASSIGNMENT(Type.ADMIN, AdminAssignment.class),
    BAN_USER(Type.ADMIN, BanUser.class),
    CHANGE_STATUS(Type.ADMIN, ChangeStatus.class),
    REMOVE_USER(Type.ADMIN, RemoveUser.class),

    SELL_SHARES(Type.USER, SellShares.class),
    BUY_SHARES(Type.USER, BuyShares.class);

    public enum Type {
        ADMIN,
        USER;
    }

    public static List<Commands> getByType(Type type) {
        List<Commands> commands = new ArrayList<Commands>();
        for (Commands command : values()) {
            if (command.type.equals(type)) {
                commands.add(command);
            }
        }
        return commands;
    }

    private final Type type;

    private final Class<? extends Command> command;

    private Commands(Type type, Class<? extends Command> command) {
        this.type = type;
        this.command = command;
    }

    public Class<? extends Command> getCommand() {
        return command;
    }

    public Command newInstance() throws Exception {
        return command.newInstance();
    }

}

To create an instance, simply use:

Commands.UPDATE_USER.newInstance();

To get all the commands for a given type:

Commands.getByType(Commands.Type.ADMIN);

Note that using this method, the Commands subclasses must implement a public nullary constructor.

Upvotes: 1

Related Questions