Pergunta

Eu estou tentando encontrar uma maneira para percorrer os valores de uma enumeração, enquanto usando os genéricos. Não tenho certeza de como fazer isso ou se é possível.

O código a seguir ilustra o que eu quero fazer. Note que o código de T.values ??() não é válido no código a seguir.

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

Aqui está como eu iria instanciar um objeto de filtro:

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

A enumeração é definida da seguinte forma:

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

Eu percebo que pode não fazer sentido para copiar os valores de um enum para uma lista, mas eu estou usando uma biblioteca que precisa de uma lista de valores como entrada e não vai funcionar com enums.


EDIT 2/5/2010:

A maioria das respostas propostas são muito semelhantes e sugiro fazer algo como isto:

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

Este seria um grande trabalho se eu posso ter certeza de que selectedOption tem um valor não nulo. Infelizmente, no meu caso de uso, este valor é muitas vezes nula, pois existe um Filtro pública () construtor sem-arg também. Isso significa que eu não posso fazer um selectedOption.getClass () sem obter um NPE. Esta classe de filtro gerencia uma lista de opções disponíveis qual das opções é selecionado. Quando nada for selecionado, selectedOption é nulo.

A única coisa que posso pensar para resolver isso é realmente passar em uma classe no construtor. Então, algo como isto:

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

Todas as idéias como fazer isso sem a necessidade de um parâmetro de classe extra nos construtores?

Foi útil?

Solução

Este é um problema difícil, de fato. Uma das coisas que você precisa fazer é java diga que você está usando um enum. Esta é declarando que você estender a classe Enum para seus genéricos. No entanto, esta classe não tem os valores () função. Então, você tem que tomar a classe para a qual você pode obter os valores.

O exemplo a seguir devem ajudá-lo a resolver o seu problema:

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

Outras dicas

Outra opção é 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);
    } 

}

Para completar, JDK8 nos dá uma maneira relativamente limpa e mais concisa de conseguir isso sem a necessidade de usar o values() synthethic na classe Enum:

Dada uma enumeração simples:

private enum TestEnum {
    A,
    B,
    C
}

E um cliente de teste:

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

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

O código pode ser trivialmente adaptado para recuperar elementos diferentes ou a recolher de uma maneira diferente.

Usando um elenco 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);
        }
    }
}

Se você tem certeza que selectedOption do Filter(T selectedOption) construtor não é nulo. Você pode usar a reflexão. Como esta.

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 isso ajude.

A raiz do problema é que você precisa para converter um array para uma lista, certo? Você pode fazer isso, usando um tipo específico (TimePeriod em vez de T), e o código a seguir.

Assim, usar algo como isto:

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

Agora você pode passar lista em qualquer método que quer uma lista.

Se você declarar Filtro como

public class Filter<T extends Iterable>

então

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

E o uso é muito 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());
        }
    }
}

Aqui abaixo um exemplo de uma classe wrapper em torno de um Enum. É um pouco bu estranho é o que eu preciso:

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

}

Para obter o valor da enumeração 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 no método acima da chamada para o método toString ().

e, em seguida, definir a enumeração com um método tal toString ().

public enum MyNameEnum {

  MR("John"), MRS("Anna");

  private String name;

  private MyNameEnum(String name) {
    this.name = name;
  }

  public String toString() {
    return this.name;
  }

}
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top