3

I am writing a simple game in which I use an Enum, CommandManager, to store information about the possible commands and what each one does. The main purposes of this Enum is to be able to print out a menu of available commands, as well as being used to check input and performing the action related to that input. My problem lies with that second use, where I am using a switch statement to determine what the user wants to do based on their input. I'm getting a compilation error when trying to use a property of an Enum (via a getter method) as a case label. The error message provided is that case expressions must be constant expressions. Given that the properties of CommandManager are declared to be final, am I right in thinking that properties of Enums simply cannot be used in switch statements? If this is the case, why?

Simplified version of the code included below in case it's an error on my end.

Method Code:

void interpretInput() { String command = input.getInput(); if (command.length() == 2) { switch (command) { case CommandManager.MAINMENU.getCommand(): goToMainMenu(); break; case CommandManager.NEWGAME.getCommand(): startNewGame(); break; case CommandManager.LISTGAMES.getCommand(): listSavedGames(); break; case CommandManager.EXITGAME.getCommand(): exitGame(); break; case CommandManager.HELPMENU.getCommand(): listAllCommands(); break; } } } 

Enum code:

public enum CommandManager { NEWGAME("!n", "New game"), MAINMENU("!m", "Go to main menu"), EXITGAME("!q", "Exit Battleships"), LISTGAMES("!g", "List saved games"), HELPMENU("!h", "Open help menu"), LOADGAME("!l", "Load a new game"), SAVEGAME("!s", "Save current game"); private final String command; private final String menuOption; CommandManager(String aCommand, String anOption) { command = aCommand; menuOption = anOption; } String getCommand() { return command; } String getMenuOption() { return menuOption; } } 
12
  • Because they are not compile time constant. Commented Oct 8, 2016 at 14:27
  • You can use Enums in switch statements, but you are not. You are using String, but as the error message says, the labels have to be constants. Commented Oct 8, 2016 at 14:27
  • 1
    After reading String command from input, it would be nice to get the according enum value and then switch on the enum directly. Commented Oct 8, 2016 at 14:34
  • You aren't putting the enum to good use if you're switching against String values. If that's your goal, the have a number of public static final String constants. You'll be able to switch against them. Commented Oct 8, 2016 at 14:37
  • If you are authoring the enum class, then it's usually a mistake to switch on it because you can write constant-specific methods for the enum constants that in nearly every case provide a more object-oriented, robust, and more easily maintainable solution. Actually, in nearly every case your first reflex after writing switch in your code should be to ask yourself "is there a way in which I can -not- switch?" Commented Oct 8, 2016 at 14:43

4 Answers 4

5

Am I right in thinking that properties of Enums simply cannot be used in switch statements?

You are.

If this is the case, why?

Because the "labels" for a switch statement need to be compile time constants, and the properties of an enum do not qualify.

The reason that they need to be compile time constants is that the compiler needs to check that the switch labels are distinct. It cannot allow something like this

switch (someValue) { case A.method(): doA(); case B.method(): doB(); } 

where A.method() and B.method() turn out to have the same value. If the case expressions are not compile-time constant expressions, then the compiler cannot detect the problem. (Method calls are never compile time constant expressions.)

Sign up to request clarification or add additional context in comments.

1 Comment

Thank you for the clear explanation as to why I was getting the error.
2

I would do things differently by using a Map to help with an action design pattern.

First add to your CommandManager enum a method that converts a command String to a CommandManager object, something like:

public static CommandManager getCommandManager(String command) { for (CommandManager cManager : CommandManager.values()) { if (command.equals(cManager.getCommand())) { return cManager; } } throw new IllegalArgumentException(command); } 

e.g.,

public enum CommandManager { NEWGAME("!n", "New game"), MAINMENU("!m", "Go to main menu"), EXITGAME("!q", "Exit Battleships"), LISTGAMES("!g", "List saved games"), HELPMENU("!h", "Open help menu"), LOADGAME("!l", "Load a new game"), SAVEGAME("!s", "Save current game"); private final String command; private final String menuOption; CommandManager(String aCommand, String anOption) { command = aCommand; menuOption = anOption; } String getCommand() { return command; } String getMenuOption() { return menuOption; } // ************ ADD THIS! ******* public static CommandManager getCommandManager(String command) { for (CommandManager cManager : CommandManager.values()) { if (command.equals(cManager.getCommand())) { return cManager; } } throw new IllegalArgumentException(command); } } 

Then give your code that uses this a Map that maps each CommandManager with a Runnable, and fill the map:

public class TestEnum { private Map<CommandManager, Runnable> commandMap = new EnumMap<>(CommandManager.class); public TestEnum() { commandMap.put(CommandManager.MAINMENU, () -> goToMainMenu()); commandMap.put(CommandManager.MAINMENU, () -> goToMainMenu()); commandMap.put(CommandManager.NEWGAME, () -> startNewGame()); commandMap.put(CommandManager.LISTGAMES, () -> listSavedGames()); commandMap.put(CommandManager.EXITGAME, () -> exitGame()); commandMap.put(CommandManager.HELPMENU, () -> listAllCommands()); } 

Then use it!

void interpretInput(String command) { CommandManager cManager = CommandManager.getCommandManager(command); commandMap.get(cManager).run(); } 

Comments

1

You have to use a bit different approach. You can write a static method inside your enum to convert command string into CommandManager enum:

public static CommandManager fromCommand(String command) { for (CommandManager commandManager : values()) { if (commandManager.getCommand().equals(command)) { return commandManager; } } return null; // or throw exception, whatever fits best for your code } 

Then you can invoke this method to get en enum object and use a switch statement to do whatever you want:

String command = input.getInput(); CommandManager commandManager = CommandManager.fromCommand(command); if (commandManager != null) { switch (commandManager) { case MAINMENU: goToMainMenu(); break; case NEWGAME: startNewGame(); break; case LISTGAMES: listSavedGames(); break; case EXITGAME: exitGame(); break; case HELPMENU: listAllCommands(); break; default: throw new IllegalArgumentException("Unknown command: " + commandManager); } } 

1 Comment

This answer, and @HovercraftFullOfEels answer, made sense to me and solved my problem so I'm marking this as the resolution. Thanks for your help, and to everyone else who contributed answers - lots of interesting stuff for me to read into.
1

Simply "CommandManager.MAINMENU.getCommand()" is not a Constant, is a function. Element in enum is Constant, so you have to convert String to enum element.

I used a map to store relation between command and enum element:

public enum CommandManager { NEWGAME("!n", "New game"), MAINMENU("!m", "Go to main menu"), EXITGAME("!q", "Exit Battleships"), LISTGAMES("!g", "List saved games"), HELPMENU("!h", "Open help menu"), LOADGAME("!l", "Load a new game"), SAVEGAME("!s", "Save current game"); private final String command; private final String menuOption; private static class InnerClass { static Map<String, CommandManager> commandMap = new HashMap<>(); } CommandManager(String aCommand, String anOption) { command = aCommand; menuOption = anOption; InnerClass.commandMap.put(aCommand, this); } public static CommandManager parseCommand(String aCommand) { return InnerClass.commandMap.get(aCommand); } String getCommand() { return command; } String getMenuOption() { return menuOption; } } 

Then change the switch code

public void interpretInput() { String command = input.getInput(); if (command.length() == 2) { CommandManager commandManager = CommandManager.parseCommand(command); if (commandManager!=null) { switch (commandManager) { case MAINMENU: goToMainMenu(); break; case NEWGAME: startNewGame(); break; case LISTGAMES: listSavedGames(); break; case EXITGAME: exitGame(); break; case HELPMENU: listAllCommands(); break; } } } } 

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.