Estoy tratando de encontrar una manera de iterar a través de los valores de una enumeración mientras uso genéricos. No estoy seguro de cómo hacer esto o si es posible.
El siguiente código ilustra lo que quiero hacer. Tenga en cuenta que el código T.values () no es válido en el siguiente código.
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);
}
}
}
Así es como crearía una instancia de un objeto Filter:
Filter<TimePeriod> filter = new Filter<TimePeriod>(TimePeriod.ALL);
La enumeración se define de la siguiente manera:
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;
}
}
Me doy cuenta de que puede que no tenga sentido copiar los valores de una enumeración a una lista, pero estoy usando una biblioteca que necesita una lista de valores como entrada y no funcionará con enumeraciones.
EDITAR 5/2/2010:
La mayoría de las respuestas propuestas son muy similares y sugieren hacer algo como esto:
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);
}
}
}
Esto funcionaría muy bien si puedo estar seguro de que selectedOption tiene un valor no nulo. Desafortunadamente, en mi caso de uso, este valor a menudo es nulo, ya que también hay un constructor público Filter () sin argumentos. Esto significa que no puedo hacer un selectedOption.getClass () sin obtener un NPE. Esta clase de filtro gestiona una lista de opciones disponibles cuál de las opciones está seleccionada. Cuando no se selecciona nada, selectedOption es nulo.
Lo único que puedo pensar para resolver esto es pasar una clase en el constructor. Entonces algo como esto:
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);
}
}
}
¿Alguna idea de cómo hacer esto sin necesidad de un parámetro Class adicional en los constructores?
fuente
getClass
código también fallará si la constante de enumeración es una subclase anónima ( ejemplo ).getDeclaringClass
debe usarse en su lugar.Respuestas:
Este es un problema realmente difícil. Una de las cosas que debe hacer es decirle a java que está usando una enumeración. Esto es indicando que amplía la clase Enum para sus genéricos. Sin embargo, esta clase no tiene la función values (). Entonces tienes que tomar la clase para la que puedas obtener los valores.
El siguiente ejemplo debería ayudarlo a solucionar su problema:
public <T extends Enum<T>> void enumValues(Class<T> enumType) { for (T c : enumType.getEnumConstants()) { System.out.println(c.name()); } }
fuente
getFriendlyName()
. Luego, tendría que agregar una interfaz a su enumeración y luego adaptar los genéricos anteriores para requerir tanto una enumeración como la interfaz para el tipoT
. La función se convierte en algo como:public <E extends Enum<?> & EnumWithFriendlyName> void friendlyEnumValues(Class<T> enumType)
Otra opción es usar 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); } }
fuente
Para completar, JDK8 nos brinda una forma relativamente limpia y más concisa de lograr esto sin la necesidad de usar el sintético
values()
en la clase Enum:Dada una enumeración simple:
private enum TestEnum { A, B, C }
Y un cliente de prueba:
@Test public void testAllValues() { System.out.println(collectAllEnumValues(TestEnum.class)); }
Esto imprimirá
{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(", " , "\"{", "}\"")); }
El código se puede adaptar trivialmente para recuperar diferentes elementos o para recopilar de una manera diferente.
fuente
Usando un yeso inseguro:
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); } } }
fuente
getClass
fallará si la constante de enumeración es una subclase anónima ( ejemplo ).getDeclaringClass
debe usarse en su lugar.Si está seguro de que
selectedOption
el constructorFilter(T selectedOption)
no es nulo. Puedes usar la reflexión. Me gusta esto.public class Filter<T> { private List<T> availableOptions = new ArrayList<T>(); private T selectedOption; public Filter(T selectedOption) { this.selectedOption = selectedOption; for (T option : this.selectedOption.getClass().getEnumConstants()) { // INVALID CODE availableOptions.add(option); } } }
Espero que esto ayude.
fuente
Para obtener el valor de la enumeración genérica:
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)); }
Tenga en cuenta en el método anterior la llamada al método toString ().
Y luego defina la enumeración con dicho método toString ().
public enum MyNameEnum { MR("John"), MRS("Anna"); private String name; private MyNameEnum(String name) { this.name = name; } public String toString() { return this.name; } }
fuente
El problema principal es que necesitas convertir una matriz en una lista, ¿verdad? Puede hacer esto, usando un tipo específico (TimePeriod en lugar de T) y el siguiente código.
Entonces usa algo como esto:
List<TimePeriod> list = new ArrayList<TimePeriod>(); list.addAll(Arrays.asList(sizes));
Ahora puede pasar la lista a cualquier método que desee una lista.
fuente
Si declara Filtro como
public class Filter<T extends Iterable>
entonces
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(); } }; } }
Y el uso es bastante sencillo:
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()); } } }
fuente
A continuación, se muestra un ejemplo de una clase contenedora en torno a un Enum. Es un poco extraño pero es lo que necesito:
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; }
}
fuente
Lo hice asi
protected List<String> enumToList(Class<? extends Enum<?>> e) { Enum<?>[] enums = e.getEnumConstants(); return Arrays.asList(enums).stream() .map(name -> name.toString()) .collect(Collectors.toList()); }
fuente