Paul Taylor
Paul Taylor

Reputation: 13110

In Java how do write code to loop round an arbitary enum?

How do write code to loop round an arbitary enum ?

So in the code below in use an enum as the values to provide to create a Html select loop, but I cannot see how to pass an enum to the method, or how to access the standard .values() and .ordinal() methods. so currently I have hardcoded a particular enum RenameFileOptions but I want to work with any enum, I could parse enum class e.g RenameFileOptions.class but still how would I access .name(), .values() and .ordinal()

public ContainerTag addCombo(UserOption userOption, int selectedValue)
{
    return div(
                label(userOption.getLabel().getMsg())
                    .withTitle(userOption.getTooltip().getMsg()),
                select(
                    each(Arrays.asList(RenameFileOptions.values()),
                            next ->
                                iffElse(next.ordinal()==selectedValue,
                                        option(next.name()).attr(Html.SELECTED, Html.SELECTED).withValue(String.valueOf(next.ordinal())),
                                        option(next.name()).withValue(String.valueOf(next.ordinal()))
                                )
                    ))
                .withName(userOption.getOption())
            );
}

Update As there seemed to be no way to achieve this in a none hacky way I instead added a getOptions() methods to each enum, and this is what is sent to my addCombo method. It means I have to essentially repeat code, which I dont like doing but it means the addCombo() method works as required and keeps the code easier to understand by not introducing difficult syntax.

public enum RenameFileOptions implements OptionList
{
    NO(TextLabel.RENAME_FILE_OPTION_NO),
    YES_IF_MATCHED_TO_RELEASE(TextLabel.RENAME_FILE_OPTION_YES_IF_RELEASE_MATCH),
    YES_IF_MATCHED_TO_RELEASE_OR_SONG(TextLabel.RENAME_FILE_OPTION_YES_IF_RELEASE_OR_SONG_MATCH),
    YES_IF_HAS_BASIC_METADATA(TextLabel.RENAME_FILE_OPTION_YES_IF_HAS_METADATA),
    YES_FOR_ALL_SONGS(TextLabel.RENAME_FILE_OPTION_YES),

    ;
    private TextLabel label;

    RenameFileOptions(TextLabel label)
    {
        this.label=label;
    }

    public String getName()
    {
        return label.getMsg();
    }

    public String toString()
    {
        return getName();
    }

    public static List<NameKey> getOptions()
    {
        List<NameKey> options = new ArrayList<NameKey>();
        for(RenameFileOptions next:RenameFileOptions.values())
        {
            options.add(new NameKey(next.ordinal(), next.getName()));
        }
        return options;
    }
}

public class NameKey
{
    private Integer id;
    private String name;

    public NameKey(Integer id, String name)
    {
        this.id =id;
        this.name=name;
    }
    public Integer getId()
    {
        return id;
    }

    public void setId(Integer id)
    {
        this.id = id;
    }

    public String getName()
    {
        return name;
    }

    public void setName(String name)
    {
        this.name = name;
    }
}

Upvotes: 0

Views: 130

Answers (3)

Artur Biesiadowski
Artur Biesiadowski

Reputation: 3698

You should use Class.getEnumConstants() method.

So, middle part of your code should be something like

                each(Arrays.asList(enumClass.getEnumConstants()),
                        next ->
                            iffElse(next.ordinal()==selectedValue,
                                    option(next.name()).attr(Html.SELECTED, Html.SELECTED).withValue(String.valueOf(next.ordinal())),
                                    option(next.name()).withValue(String.valueOf(next.ordinal()))
                            )
                ))

and you should pass Class<? extends Enum<?>> enumClass in method parameters.

Upvotes: 5

slim
slim

Reputation: 41223

You can use EnumSet.allOf():

for(MyType t : EnumSet.allOf(MyType.class)) {
    doSomethingWith(t);
}

... or with Java 8 lambdas:

EnumSet.allOf(MyType.class).forEach( 
    t -> doSomethingWith(t)
);

Despite being a Set, EnumSet's documentation states that its iterator yields values "in their natural order (the order in which the enum constants are declared)"


For an arbitarary enum class:

<T extends Enum<T>> void loopAroundEnum(Class<T> enumType) {
    EnumSet.allOf(enumType).forEach(
       val -> doSomethingWith(val) // val is of type T
    );
}

Although the Java habit is to not optimise prematurely, it's worth knowing that an EnumSet is probably more efficient both in speed and in memory consumption than using the MyType[] array returned by MyType.getEnumConstants().

From EnumSet's Javadoc:

Enum sets are represented internally as bit vectors. This representation is extremely compact and efficient. The space and time performance of this class should be good enough to allow its use as a high-quality, typesafe alternative to traditional int-based "bit flags."

Upvotes: 3

Joop Eggen
Joop Eggen

Reputation: 109557

As Enum is the base class, and you need to pass a Class to an enum:

public <E extends Enum<E>> void method(Class<E> type) {
    E[] values = type.getEnumConstants();
}

Of course Class has a test whether it is an enum; it returns null when no enum class.

Upvotes: 1

Related Questions