2010-02-05 17 views
53

Estoy tratando de encontrar una forma 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.Iterar los valores de enum usando genéricos de Java

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

Aquí es cómo iba a crear una instancia de un objeto de filtro:

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

La enumeración se define como sigue:

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 di cuenta de que podría no tener sentido para 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 2/5/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 sería un gran trabajo si puedo estar seguro de que selectedOption tiene un valor no nulo. Desafortunadamente, en mi caso de uso, este valor es a menudo nulo, ya que hay un filtro público() constructor no-arg también. Esto significa que no puedo hacer una selectedOption.getClass() sin obtener un NPE. Esta clase de filtro administra una lista de opciones disponibles que de las opciones se selecciona. Cuando no se selecciona nada, selectedOption es nulo.

Lo único que puedo pensar para resolver esto es pasar realmente 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 la necesidad de un parámetro de clase adicional en los constructores?

+1

El código 'getClass' también fallará si la constante enum es una subclase anónima ([ejemplo] (https://ideone.com/ccjW7w)). ['getDeclaringClass'] (http://docs.oracle.com/javase/8/docs/api/java/lang/Enum.html#getDeclaringClass--) debería usarse en su lugar. – Radiodef

Respuesta

85

Este es un problema difícil de hecho. Una de las cosas que debes hacer es decirle a Java que estás usando una enumeración. Esto es al afirmar que extiendes la clase Enum para tus genéricos. Sin embargo, esta clase no tiene la función de valores(). Entonces debes tomar la clase para la cual puedes obtener los valores.

El siguiente ejemplo debería ayudar a solucionar su problema:

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

Dado que todas estas respuestas son básicamente las mismas, selecciono la suya porque fue la primera. No es una solución a la pregunta complementaria en la que edité mi pregunta, pero me permitió resolver mis necesidades inmediatas y seguir adelante. ¡Gracias! – Tauren

+0

¿El c.name() no devuelve el nombre del elemento enum? ¿Qué hay de devolver su valor? Digamos, por un elemento QUIZ ("cuestionario"), devolviendo el "cuestionario" y no el QUIZ. – Stephane

+0

Para eso, necesitaría tener un método en la enumeración que devuelva el valor, por ejemplo: 'getFriendlyName()'. Entonces tendría que agregar una interfaz a su enumeración y luego adaptar los genéricos anteriores para requerir una enumeración Y la interfaz para el tipo 'T'. La función se convierte en algo así como: 'public & EnumWithFriendlyName> void friendlyEnumValues ​​(Clase enumType)' – Thirler

0

La raíz del problema es que se necesita para convertir una matriz a una lista, ¿verdad? Puede hacerlo, utilizando un tipo específico (TimePeriod en lugar de T) y el siguiente código.

a fin de utilizar algo como esto:

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

Ahora puede pasar a la lista de cualquier método que quiere una lista.

1

Si está seguro de que selectedOption del constructor Filter(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.

4

Usando un reparto insegura:

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); 
     } 
    } 
} 
+0

'getClass' fallará si la constante enum es una subclase anónima ([ejemplo] (https://ideone.com/ccjW7w)). ['getDeclaringClass'] (http://docs.oracle.com/javase/8/docs/api/java/lang/Enum.html#getDeclaringClass--) debería usarse en su lugar. – Radiodef

7

Otra opción es utilizar 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); 
    } 

} 
0

Si se declara como filtro

public class Filter<T extends Iterable> 

continuación

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 su uso es muy fácil:

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()); 
     } 
    } 
} 
+0

Enfoque interesante. Creo que esto funcionaría sin necesidad de declarar la clase . Sin embargo, preferiría no tener que cambiar todas mis enumeraciones, por lo que me inclino por un método diferente. – Tauren

0

A continuación un ejemplo de una clase envoltura alrededor de una enumeración. Es un bu raro poco 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; 
} 

}

3

Para completar, JDK8 nos da una manera relativamente limpio y más concisa de lograr esto sin la necesidad de utilizar el synthethic values() en Enum clase:

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(", " , "\"{", "}\"")); 
} 

Código se puede adaptar trivial para recuperar diferentes elementos o para recoger de una manera diferente.

0

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

Nota en el método por encima de 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; 
    } 

} 
Cuestiones relacionadas