Iterate enum values using java generics

JavaGenericsEnumsEnumeration

Java Problem Overview


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?

Java Solutions


Solution 1 - Java

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 - Java

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 - Java

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 - Java

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 - Java

If you are sure that selectedOption of the constructor Filter(T selectedOption) is not null. You can use reflection. Like this.

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



public Filter(T selectedOption) {
    this.selectedOption = selectedOption;
    for (T option : <b>this.selectedOption.getClass().getEnumConstants()</b>) {  // INVALID CODE
        availableOptions.add(option);
    }
}




}

}

Hope this helps.

Solution 6 - Java

If you declare Filter as

public class Filter<T extends Iterable>

then

import java.util.Iterator;

public enum TimePeriod implements Iterable {
    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;
    }

    public Iterator<TimePeriod> iterator() {
        return new Iterator<TimePeriod>() {

            private int index;

            @Override
            public boolean hasNext() {
                return index < LAST30DAYS.ordinal();
            }

            @Override
            public TimePeriod next() {
                switch(index++) {
                    case	0	: return	    ALL;
                    case	1	: return	    FUTURE;
                    case	2	: return	    NEXT7DAYS;
                    case	3	: return	    NEXT14DAYS;
                    case	4	: return	    NEXT30DAYS;
                    case	5	: return	    PAST;
                    case	6	: return	    LAST7DAYS;
                    case	7	: return	    LAST14DAYS;
                    case	8	: return	    LAST30DAYS;
                    default: throw new IllegalStateException();
                }
            }

            @Override
            public void remove() {
                throw new UnsupportedOperationException();
            }
        };
    }
}

And usage is quite easy:

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

    public Filter(T selectedOption) {
        this.selectedOption = selectedOption;
        Iterator<TimePeriod> it = selectedOption.iterator();
        while(it.hasNext()) {
            availableOptions.add(it.next());
        }
    }
}

Solution 7 - Java

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;
  }
  
}

Solution 8 - Java

I did it like this

    protected List<String> enumToList(Class<? extends Enum<?>> e) {
    		Enum<?>[] enums = e.getEnumConstants();
    		return Arrays.asList(enums).stream()
                   .map(name -> name.toString())
                   .collect(Collectors.toList());
    	}

Solution 9 - Java

The root problem is that you need to convert an array to a list, right? You can do this, by using a specific type (TimePeriod instead of T), and the following code.

So use something like this:

List<TimePeriod> list = new ArrayList<TimePeriod>();
list.addAll(Arrays.asList(sizes));

Now you can pass list into any method that wants a list.

Solution 10 - Java

Here below an example of a wrapper class around an Enum. Is a little bit weird bu is what i need :

public class W2UIEnum<T extends Enum<T> & Resumable> {

public String id;
public String caption;

public W2UIEnum(ApplicationContext appContext, T t) {
	this.id = t.getResume();
	this.caption = I18N.singleInstance.getI18nString(t.name(), "enum_"
			+ t.getClass().getSimpleName().substring(0, 1).toLowerCase()
			+ t.getClass().getSimpleName().substring(1,
					t.getClass().getSimpleName().length()), appContext
			.getLocale());
}

public static <T extends Enum<T> & Resumable> List<W2UIEnum<T>> values(
		ApplicationContext appContext, Class<T> enumType) {
	List<W2UIEnum<T>> statusList = new ArrayList<W2UIEnum<T>>();
	for (T status : enumType.getEnumConstants()) {
		statusList.add(new W2UIEnum(appContext, status));
	}
	return statusList;
}

}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionTaurenView Question on Stackoverflow
Solution 1 - JavaThirlerView Answer on Stackoverflow
Solution 2 - JavaMiserable VariableView Answer on Stackoverflow
Solution 3 - JavaquantumView Answer on Stackoverflow
Solution 4 - JavaBozhoView Answer on Stackoverflow
Solution 5 - JavaNawaManView Answer on Stackoverflow
Solution 6 - JavaBoris PavlovićView Answer on Stackoverflow
Solution 7 - JavaStephaneView Answer on Stackoverflow
Solution 8 - JavaPradeep SreeramView Answer on Stackoverflow
Solution 9 - JavaSteve McLeodView Answer on Stackoverflow
Solution 10 - JavaBenjamin FuentesView Answer on Stackoverflow