1

I have the following enumerator:

public enum UserChoice {
    QUIT, LIST_BOOKS, CHECKOUT_BOOK, RETURN_BOOK, LIST_MOVIES,
    CHECKOUT_MOVIE, RETURN_MOVIE, USER_INFORMATION
}

and I would like to use it in a switch statement which takes an int as a parameter. However, I need to get the int value of an enum, so I am doing this:

try {
    int option = Reader.getUserOption();
} catch (InputMismatchException ex) {
    option = 8;
}

switch (option) {
    case UserChoice.QUIT.ordinal():
        break;
    case UserChoice.LIST_BOOKS.ordinal():
         Printer.printBooks(library);
         break;
    case UserChoice.CHECKOUT_BOOK.ordinal():
         // code
         break;
    case UserChoice.RETURN_BOOK.ordinal():
         // code
         break;
    case UserChoice.LIST_MOVIES.ordinal():
         Printer.printMovies(library);
         break;
    case UserChoice.CHECKOUT_MOVIE.ordinal():
         // code
         break;
    case UserChoice.RETURN_MOVIE.ordinal():
         // code
         break;
    case UserChoice.USER_INFORMATION.ordinal():
         System.out.println(currentUser);
         break;
    default:
         Printer.printInvalidOptionMessage();
         break;
    }

Is there any way to cast an int into an enumeration value or who can I achieve this using enumerations. In the end, my point is to have the name of the enumerator for each case, so that I can clearly understand what each case is doing because previously I was using int values to do it.

ΦXocę 웃 Пepeúpa ツ
  • 47,427
  • 17
  • 69
  • 97
lmiguelvargasf
  • 63,191
  • 45
  • 217
  • 228
  • In the end what I have done following @Darth Android, @Xoce's, and @Jim Garrison responses is change from `int` to `UserChoice`, so `UserChoice option = UserChoice.values()[Reader.getUserOption()];`, and set `option` to `INVALID_OPTION` (a new value added to the enum) in the catch block. – lmiguelvargasf Mar 18 '16 at 18:54

5 Answers5

3

Create an additional enum value for INVALID_CHOICE and use that. But more than that, you shold completely decouple the user input from the ordering (and ordinal value) of the enum.

Here's an example of how to do that.

public static enum UserChoice {
    /*
     * Establish the mapping between the enum value (the semantic action)
     * and the user's input.  This can be adapted to whatever form the user
     * input takes and is decoupled from the ordinal values.  It's all in 
     * one place here and a change here does not need a change anywhere else.
     */
    QUIT            ( 0), 
    LIST_BOOKS      ( 1), 
    CHECKOUT_BOOK   ( 2), 
    RETURN_BOOK     ( 3), 
    LIST_MOVIES     ( 4),
    CHECKOUT_MOVIE  ( 5), 
    RETURN_MOVIE    ( 6), 
    USER_INFORMATION( 7),
    INVALID_CHOICE  (Integer.MIN_VALUE);

    /*
     * The mapping, and its initialization, using the new features in Java 8
     */
    private static final Map<Integer,UserChoice> valueMap = Arrays.stream(UserChoice.values()).collect(Collectors.toMap(UserChoice::getValue, Function.identity()));

    /*
     * A method to convert from user input (int in this case) to the corresponding
     * enum value based on the mapping above.
     */
    public static UserChoice fromUserInput(int input) {
        return Optional.ofNullable(valueMap.get(input)).orElse(INVALID_CHOICE);
    }

    /*
     * Per-enum value and method
     */
    private final int userValue;
    private UserChoice(int userValue) { this.userValue = userValue; }
    public int getValue() { return this.userValue; }
}

/*
 * Simple test
 */
public static void main(String args[]) throws Exception 
{
    for (int i=0; i<10; i++)
    {
        UserChoice c = UserChoice.fromUserInput(i);
        System.out.printf("Input %d enum is %s\n", i, c.toString());
    }
}   
Jim Garrison
  • 85,615
  • 20
  • 155
  • 190
  • option is an int, that a user enters through the console – lmiguelvargasf Mar 18 '16 at 18:31
  • 1
    I've rewritten my example to show how to do this in a way that completely decouples the user input from the ordinal position. While you could use the ordinal, my example completely encapsulates the mapping from user input to enum value, and can be adapted no matter how the user inputs their choice. – Jim Garrison Mar 18 '16 at 23:35
  • You are totally right @Jim Garrison, so your answer deserves to be validated – lmiguelvargasf Mar 18 '16 at 23:37
  • 1
    Thanks. This question made me research the new Java 8 streams stuff. A little mind-bending but really cool. BTW, this has been tested in Eclipse.. Have fun! – Jim Garrison Mar 18 '16 at 23:39
2

UserChoice.values()[option] should do it. You can separately determine that option >= 0 and option < UserChoice.values().length.

Beware that there are a lot of resources advocating against the use or storage of ordinal, because the numbers will all change if you add, remove, or reorder your enum values. If the number is an inherent part of your enum—like RETURN_MOVIE should always resolve to option 6—you might want to make it a constructor parameter and property of the enum constant, and provide lookup through a separate map.

Jeff Bowman
  • 90,959
  • 16
  • 217
  • 251
1

If Reader.getUserOption() returns an option from UserChoice Enumerator then no cast is needed...

Example:

switch (Reader.getUserOption()) {
            case CHECKOUT_MOVIE:
                
                break;
            case LIST_BOOKS:
                
                break;
            case QUIT:
                
                break;

            default:
                break;
        }

you will need to do the switch case inside a try-catch of course.

Edit:

If the method getUserOption returns an int and that int is the Ordinal representation of the options in the enumerator:

QUIT, ..........., USER_INFORMATION

0,..............., 7

then do this:

switch (UserChoice.values()[Reader.getUserOption()]) {
Community
  • 1
  • 1
ΦXocę 웃 Пepeúpa ツ
  • 47,427
  • 17
  • 69
  • 97
0

Please try UserChoice.values()[option] where option starts from 0 and option<UserChoice.values().length

Zaid Qureshi
  • 1,203
  • 1
  • 10
  • 15
  • the point is that I want to see in the code the name of the enum value. Otherwise, I prefer to use what I had previously (a number placed directly in the code) – lmiguelvargasf Mar 18 '16 at 18:36
0

You need to lookup the enum by index:

try {
    int option = Reader.getUserOption();
} catch (InputMismatchException ex) {
    option = 8;
}

switch (UserChoice.values()[option]) {
    case QUIT:
        break;
    case LIST_BOOKS:
         Printer.printBooks(library);
         break;
    case CHECKOUT_BOOK:
         // code
         break;
    case RETURN_BOOK:
         // code
         break;
    case LIST_MOVIES:
         Printer.printMovies(library);
         break;
    case CHECKOUT_MOVIE:
         // code
         break;
    case RETURN_MOVIE:
         // code
         break;
    case USER_INFORMATION:
         System.out.println(currentUser);
         break;
    default:
         Printer.printInvalidOptionMessage();
         break;
    }

I would also recommend moving away from storing the index and store the enum name as a string instead, because the ordinals can change or shift when values are added/removed/moved in the enum.

Darth Android
  • 3,437
  • 18
  • 19
  • Thank you! Your answer is really good, and solves everything. – lmiguelvargasf Mar 18 '16 at 18:49
  • 1
    This breaks if you change the order or number of options. You should have the conversion of input to enum in the enum so it's all in one place. The enum should have a static method to lookup the input and return the enum value, I.e. `UserChoice.fromUserInput(String input)` – Jim Garrison Mar 18 '16 at 20:01