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?

57
demandé sur Tauren 2010-02-05 11:32:04

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());
        }
}
91
répondu Thirler 2010-02-05 08:44:38

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

}
8
répondu Miserable Variable 2010-02-05 08:55:21

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);
        }
    }
}
4
répondu Bozho 2010-02-05 08:48:38

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.

4
répondu quantum 2015-09-22 14:40:37

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.

1
répondu NawaMan 2010-02-05 08:48:25

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.

0
répondu Steve McLeod 2010-02-05 08:47:26

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());
        }
    }
}
0
répondu Boris Pavlović 2010-02-05 08:58:47

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

}

0
répondu Benjamin Fuentes 2014-03-07 08:37:38

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

}
0
répondu Stephane 2015-12-08 10:23:24