2

I have a lot of classes which define some Enum whose values I want loaded from a user-entered String. So I find myself repeating the method:

public final class Status {
    public static enum TYPE { Slow, Haste, Sleep, Stop, Stone, Invis, Rage,
        Conf, Bleed, Weak, Dumb, Sil, Rot, Lev, Clumsy };

    public static Set<Status.TYPE> typesFromString(String string) { 
        EnumSet<Status.TYPE> set = EnumSet.noneOf(Status.TYPE.class);
        if (string == null)
            return set;
        String[] elements = string.split(",");
        for (String element : elements) {
            element = element.trim();
            for (TYPE type : EnumSet.allOf(Status.TYPE.class)) {
                if (type.toString().equalsIgnoreCase(element)) {
                    set.add(type);
                    break;
                }
            }
        }
        return set;
    }

Which is to say, given a string which contains comma-separated elements which match the enum entries, return a set populated with each match.

I'd love to make this generic, so I wouldn't have to maintain ten different copies of it, but I can't quite figure out how to make this generic while returning a set of enums. I think it would look vaguely like the following method:

public static Set<[Enum Class Specified in Argument]> setFromString(String string, [Class of Enum to Work With]) {
    Set<E extends Enum<E>> set = EnumSet.noneOf([Class of Enum]);
    if (string == null)
        return set;
    for (String element : string.split(",")) {
        element = element.trim().toLowerCase();
        for ([Element of Enum] type : EnumSet.allOf([Class of Enum])) {
            if (type.toString().trim().toLowerCase().equals(element))
                set.add(type);
        }
    }
}
return set;
4
  • Unrelated comment: the code convention to enum types is to use uppercase (i.e. SLOW, HASTE, SLEEP, STOP... instead of Slow, Haste, Sleep, Stop...) Commented Nov 16, 2014 at 17:57
  • Agreed, but I'd also like to display from these values directly, and I don't want the user seeing all-caps. If there's any way to have the best of both worlds, I'm all ears. Commented Nov 16, 2014 at 18:01
  • 1
    Then here: use uppercase for the enum constants, and then use set.add(Enum.valueOf(myEnumClazz, element.toUpperCase())), which also avoids the loop. There's prebuilt utilities for getting an enum constant by name. Commented Nov 16, 2014 at 18:16
  • You misunderstand -- I want to later be able to call toString() on a Status.TYPE and display the results as I currently have them typed. If I convert the enum entries to upper-case, I'll get upper-case output. Commented Nov 16, 2014 at 18:27

1 Answer 1

6

You need to specify the generic parameter (<E extends Enum<E>) between the modifiers (public static) and the return value (Set<E>). And pass as an argument the Class of the enum (Class<E> clazz`). Which should look something like this:

    public static <E extends Enum<E>> Set<E> parseValues(
        String string, Class<E> clazz
    ) { 
        EnumSet<E> set = EnumSet.noneOf(clazz);
        if (string == null) {
            return set; // ( better, NPE )
        }
        String[] elements = string.split(",");
        for (String element : elements) {
            element = element.trim();
            for (E type : EnumSet.allOf(clazz)) {
                if (type.name().equalsIgnoreCase(element)) {
                    set.add(type);
                    break;
                }
            }
            // ( Do we really want to ignore spurious values? )
        }
        return set;
    }

(Not tested or compiled.)

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

1 Comment

Yep, that did the trick. I guess I need to brush up on my generics, since I get the gist of what's going on when I read it but I was trying to define it with the signature public static Set<E extends Enum<E>> which was freaking Java out. Thanks. (Also, the comment about spurious values is great for posterity. I'm still thinking about it in my use case.)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.