Iterate enum values using java generics

48,966

Solution 1

This is a hard problem indeed. One of the things you need to do is tell java that you are using an enum. This is by stating that you extend the Enum class for your generics. However this class doesn't have the values() function. So you have to take the class for which you can get the values.

The following example should help you fix your problem:

public <T extends Enum<T>> void enumValues(Class<T> enumType) {
        for (T c : enumType.getEnumConstants()) {
             System.out.println(c.name());
        }
}

Solution 2

Another option is to use EnumSet:

class PrintEnumConsants {

    static <E extends Enum <E>> void foo(Class<E> elemType) {
        for (E e : java.util.EnumSet.allOf(elemType)) {
            System.out.println(e);
        }
    }

    enum Color{RED,YELLOW,BLUE};
    public static void main(String[] args) {
        foo(Color.class);
    } 

}

Solution 3

For completeness, JDK8 gives us a relatively clean and more concise way of achieving this without the need to use the synthethic values() in Enum class:

Given a simple enum:

private enum TestEnum {
    A,
    B,
    C
}

And a test client:

@Test
public void testAllValues() {
    System.out.println(collectAllEnumValues(TestEnum.class));
}

This will print {A, B, C}:

public static <T extends Enum<T>> String collectAllEnumValues(Class<T> clazz) {
    return EnumSet.allOf(clazz).stream()
            .map(Enum::name)
            .collect(Collectors.joining(", " , "\"{", "}\""));
}

Code can be trivially adapted to retrieve different elements or to collect in a different way.

Solution 4

Using an unsafe cast:

class Filter<T extends Enum<T>> {
    private List<T> availableOptions = new ArrayList<T>();
    private T selectedOption;

    public Filter(T selectedOption) {
        Class<T> clazz = (Class<T>) selectedOption.getClass();
        for (T option : clazz.getEnumConstants()) {
            availableOptions.add(option);
        }
    }
}

Solution 5

To get the value of the generic enumeration:

  protected Set<String> enum2set(Class<? extends Enum<?>> e) {
    Enum<?>[] enums = e.getEnumConstants();
    String[] names = new String[enums.length];
    for (int i = 0; i < enums.length; i++) {
      names[i] = enums[i].toString();
    }
    return new HashSet<String>(Arrays.asList(names));
  }

Note in the above method the call to the toString() method.

And then define the enumeration with such a toString() method.

public enum MyNameEnum {

  MR("John"), MRS("Anna");

  private String name;

  private MyNameEnum(String name) {
    this.name = name;
  }

  public String toString() {
    return this.name;
  }

}
Share:
48,966
Tauren
Author by

Tauren

Software engineer

Updated on July 26, 2022

Comments

  • Tauren
    Tauren almost 2 years

    I'm trying to find a way to iterate through an enum's values while using generics. Not sure how to do this or if it is possible.

    The following code illustrates what I want to do. Note that the code T.values() is not valid in the following code.

    public class Filter<T> {
        private List<T> availableOptions = new ArrayList<T>();
        private T selectedOption;
    
        public Filter(T selectedOption) {
            this.selectedOption = selectedOption;
            for (T option : T.values()) {  // INVALID CODE
                availableOptions.add(option);
            }
        }
    }
    

    Here is how I would instantiate a Filter object:

    Filter<TimePeriod> filter = new Filter<TimePeriod>(TimePeriod.ALL);
    

    The enum is defined as follows:

    public enum TimePeriod {
        ALL("All"), 
        FUTURE("Future"), 
        NEXT7DAYS("Next 7 Days"), 
        NEXT14DAYS("Next 14 Days"), 
        NEXT30DAYS("Next 30 Days"), 
        PAST("Past"),
        LAST7DAYS("Last 7 Days"), 
        LAST14DAYS("Last 14 Days"),
        LAST30DAYS("Last 30 Days"); 
    
        private final String name;
    
        private TimePeriod(String name) {
            this.name = name;
        }
    
        @Override
        public String toString() {
            return name;
        }
    }
    

    I realize it might not make sense to copy a enum's values to a list, but I'm using a library that needs a list of values as input and won't work with enums.


    EDIT 2/5/2010:

    Most of the answers proposed are very similar and suggest doing something like this:

    class Filter<T extends Enum<T>> {
        private List<T> availableOptions = new ArrayList<T>();
        private T selectedOption;
    
        public Filter(T selectedOption) {
            Class<T> clazz = (Class<T>) selectedOption.getClass();
            for (T option : clazz.getEnumConstants()) {
                availableOptions.add(option);
            }
        }
    }
    

    This would work great if I can be sure that selectedOption has a non-null value. Unfortunately, in my use case, this value is often null, as there is a public Filter() no-arg constructor as well. This means I can't do a selectedOption.getClass() without getting an NPE. This filter class manages a list of available options which of the options is selected. When nothing is selected, selectedOption is null.

    The only thing I can think to solve this is to actually pass in a Class in the constructor. So something like this:

    class Filter<T extends Enum<T>> {
        private List<T> availableOptions = new ArrayList<T>();
        private T selectedOption;
    
        public Filter(Class<T> clazz) {
            this(clazz,null);
        }
    
        public Filter(Class<T> clazz, T selectedOption) {
            this.selectedOption = selectedOption;
            for (T option : clazz.getEnumConstants()) {
                availableOptions.add(option);
            }
        }
    }
    

    Any ideas how to do this without needing an extra Class parameter in the constructors?

  • Tauren
    Tauren over 14 years
    Interesting approach. I think this would work without needing to have Class<T> declared. However, I'd prefer to not have to change all of my enums, so I'm leaning toward a different method.
  • Stephane
    Stephane over 8 years
    Isn't the c.name() returning the name of the enum item ? How about returning its value ? Say, for a QUIZ("quizzie") item, returning the "quizzie" and not the QUIZ.
  • Thirler
    Thirler over 8 years
    For that you would need to have a method on the enum that returns the value say: getFriendlyName(). Then you'd have to add an interface to your enum and then adapt the generics above to require both an enum AND the interface for type T. The function then becomes something like: public <E extends Enum<?> & EnumWithFriendlyName> void friendlyEnumValues(Class<T> enumType)
  • Radiodef
    Radiodef over 6 years
    getClass will fail if the enum constant is an anonymous subclass (example). getDeclaringClass should be used instead.
  • Sonali Gupta
    Sonali Gupta almost 3 years
    Can I get some detail as to how to write the interface for type T or any link where I can find an example because I am looking to retrieve values.
  • Thirler
    Thirler almost 3 years
    @SonaliGupta it's the same mechanism as any other interface. You define an interface with your function getFriendlyName(), then in the enum you implement the interface using 'implements EnumWithFriendlyName`' and your function that wants to use the enum's interface functions you do what I show in my previous comment.
  • Marco Sulla
    Marco Sulla almost 3 years
    Simple and elegant :)