Itérer les valeurs enum en utilisant des génériques java
J'essaie de trouver un moyen d'itérer à travers les valeurs d'une énumération tout en utilisant des génériques. Je ne sais pas comment faire cela ou si c'est possible.
Le code suivant illustre ce que je veux faire. Notez que le code T. values () n'est pas valide dans le code suivant.
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);
}
}
}
Voici comment j'instancierais un objet Filter:
Filter<TimePeriod> filter = new Filter<TimePeriod>(TimePeriod.ALL);
L'énumération est définie comme suit:
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;
}
}
Je me rends compte que cela pourrait ne pas avoir de sens de copier les valeurs d'une énumération dans une liste, mais j'utilise un bibliothèque qui a besoin d'une liste de valeurs en entrée et ne fonctionnera pas avec les énumérations.
Édition 2/5/2010:
La plupart des réponses proposées sont très similaires et suggèrent de faire quelque chose comme ceci:
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);
}
}
}
Cela fonctionnerait très bien si je peux être sûr que selectedOption a une valeur non nulle. Malheureusement, dans mon cas d'utilisation, cette valeur est souvent nulle, comme il est un public Filtre() non-arg constructeur ainsi. Cela signifie que je ne peux pas faire une option selectedOption.getClass () sans obtenir un NPE. Cette classe de filtre gère une liste d'options disponibles les options est sélectionnée. Lorsque rien n'est sélectionné, selectedOption est null.
La seule chose que je peux penser pour résoudre ceci est de passer réellement dans une classe dans le constructeur. Donc quelque chose comme ceci:
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);
}
}
}
Des idées pour le faire sans avoir besoin d'un paramètre de classe supplémentaire dans les constructeurs?
9 réponses
C'est un problème difficile en effet. Une des choses que vous devez faire est de dire à java que vous utilisez une énumération. C'est en indiquant que vous étendez la classe Enum pour vos génériques. Cependant, cette classe n'a pas la fonction values (). Vous devez donc prendre la classe pour laquelle vous pouvez obtenir les valeurs.
L'exemple suivant devrait vous aider à résoudre votre problème:
public <T extends Enum<T>> void enumValues(Class<T> enumType) {
for (T c : enumType.getEnumConstants()) {
System.out.println(c.name());
}
}
Une autre option consiste à utiliser 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);
}
}
Utilisation d'une distribution non sécurisée:
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);
}
}
}
Pour être complet, JDK8 nous donne un moyen relativement propre et plus concis d'y parvenir sans avoir besoin d'utiliser le synthethic values()
dans la classe Enum:
Étant donné une énumération simple:
private enum TestEnum {
A,
B,
C
}
Et un client de test:
@Test
public void testAllValues() {
System.out.println(collectAllEnumValues(TestEnum.class));
}
Cela va imprimer {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(", " , "\"{", "}\""));
}
Le Code peut être trivialement adapté pour récupérer différents éléments ou pour collecter d'une manière différente.
Si vous êtes sûr que selectedOption
du constructeur Filter(T selectedOption)
n'est pas null. Vous pouvez utiliser la réflexion. Pareil.
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);
}
}
}
J'espère que cela aide.
Le problème racine est que vous devez convertir un tableau en liste, n'est-ce pas? Vous pouvez le faire, en utilisant un type spécifique (TimePeriod au lieu de T), et le code suivant.
Utilisez donc quelque chose comme ceci:
List<TimePeriod> list = new ArrayList<TimePeriod>();
list.addAll(Arrays.asList(sizes));
Maintenant, vous pouvez passer list dans n'importe quelle méthode qui veut une liste.
Si vous déclarez filtre comme
public class Filter<T extends Iterable>
Puis
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();
}
};
}
}
Et l'utilisation est assez facile:
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());
}
}
}
Voici ci-dessous un exemple de classe wrapper autour d'une énumération. Est un peu bizarre bu est ce dont j'ai besoin:
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;
}
}
Pour obtenir la valeur de l'énumération Générique:
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));
}
Notez dans la méthode ci-dessus l'appel à la méthode toString ().
Puis définissez l'énumération avec une telle méthode toString ().
public enum MyNameEnum {
MR("John"), MRS("Anna");
private String name;
private MyNameEnum(String name) {
this.name = name;
}
public String toString() {
return this.name;
}
}