web-dev-qa-db-fra.com

Itérer des valeurs enum à l'aide de génériques Java

J'essaie de trouver un moyen de parcourir les valeurs d'une énumération en utilisant des génériques. Vous ne savez 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 instancier un objet filtre:

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 bien compte que cela n’a pas de sens de copier les valeurs d’une énumération dans une liste, mais j’utilise une bibliothèque qui nécessite une liste de valeurs en entrée et ne fonctionne pas avec des énumérations.


EDIT 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, car il existe également un constructeur public Filter () no-arg. Cela signifie que je ne peux pas faire de selectedOption.getClass () sans obtenir un NPE. Cette classe de filtre gère une liste d'options disponibles, laquelle des options est sélectionnée. Lorsque rien n'est sélectionné, selectedOption est null.

La seule chose à laquelle je peux penser pour résoudre ce problème est de passer réellement une classe dans le constructeur. Donc, quelque chose comme ça:

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 comment faire cela sans avoir besoin d'un paramètre de classe supplémentaire dans les constructeurs?

60
Tauren

C'est un problème difficile en effet. Une des choses à faire est de dire à Java que vous utilisez une énumération. C'est en déclarant que vous étendez la classe Enum pour vos génériques. Cependant, cette classe n'a pas la fonction values ​​(). Vous devez donc suivre le cours pour lequel 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());
        }
}
98
Thirler

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
Miserable Variable

Pour être complet, JDK8 nous offre un moyen relativement propre et plus concis d’y parvenir sans avoir à utiliser la fonction synthétique values() dans la classe Enum:

Donné une simple énumération:

private enum TestEnum {
    A,
    B,
    C
}

Et un client test:

@Test
public void testAllValues() {
    System.out.println(collectAllEnumValues(TestEnum.class));
}

Ceci imprimera {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 adapté de manière triviale pour récupérer différents éléments ou pour les collecter de manière différente. 

5
quantum

En utilisant un casting dangereux:

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
Bozho

Si vous êtes sûr que selectedOption du constructeur Filter(T selectedOption) n'est pas null. Vous pouvez utiliser la réflexion. Comme ça.

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 t'aides.

1
NawaMan

Le problème fondamental 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.

Alors utilisez quelque chose comme ceci:

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

Maintenant, vous pouvez passer liste dans n'importe quelle méthode qui veut une liste.

0
Steve McLeod

Si vous déclarez le filtre en tant que

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
Boris Pavlović

Voici ci-dessous un exemple de classe wrapper autour d'un Enum . C'est un peu bizarre mais c'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
Benjamin Fuentes

Pour obtenir le 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 ().

Et ensuite définir 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
Stephane