Подтвердить что ты не робот

Итерировать значения enum с использованием дженериков java

Я пытаюсь найти способ итерации значений enum при использовании generics. Не знаете, как это сделать или если это возможно.

Следующий код иллюстрирует то, что я хочу сделать. Обратите внимание, что код T.values ​​() недопустим в следующем коде.

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

Вот как бы я создал экземпляр объекта Filter:

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

Перечисление определяется следующим образом:

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

Я понимаю, что нет смысла копировать значения перечисления в список, но я использую библиотеку, которая нуждается в списке значений в качестве входных данных и не будет работать с перечислениями.


ИЗМЕНИТЬ 2/5/2010:

Большинство предложенных ответов очень похожи и предлагают сделать что-то вроде этого:

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

Это будет отлично работать, если я могу быть уверен, что selectedOption имеет ненулевое значение. К сожалению, в моем случае использования это значение часто является нулевым, так как существует конструктор public Filter() no-arg. Это означает, что я не могу сделать selectedOption.getClass() без получения NPE. Этот класс фильтра управляет списком доступных опций, который выбран из этих опций. Когда ничего не выбрано, выбрано значениеOption равно null.

Единственное, что я могу решить, это фактически передать класс в конструкторе. Так что-то вроде этого:

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

Любые идеи, как это сделать, не требуя дополнительного параметра класса в конструкторах?

4b9b3361

Ответ 1

Это действительно тяжелая проблема. Одна из вещей, которую вам нужно сделать, это указать java, что вы используете перечисление. Это означает, что вы расширяете класс Enum для своих дженериков. Однако этот класс не имеет функции values ​​(). Поэтому вам нужно взять класс, для которого вы можете получить значения.

Следующий пример поможет вам исправить вашу проблему:

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

Ответ 2

Другой вариант - использовать 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);
    } 

}

Ответ 3

Использование небезопасного действия:

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

Для полноты JDK8 дает нам относительно чистый и более сжатый способ достижения этого без необходимости использования синтетического values() в классе Enum:

Для простого перечисления:

private enum TestEnum {
    A,
    B,
    C
}

И тестовый клиент:

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

Откроется {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(", " , "\"{", "}\""));
}

Код может быть тривиально адаптирован для извлечения разных элементов или для сбора другим способом.

Ответ 5

Если вы уверены, что selectedOption конструктора Filter(T selectedOption) не является нулевым. Вы можете использовать отражение. Вот так.

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

Надеюсь, что это поможет.

Ответ 6

Корневая проблема заключается в том, что вам нужно преобразовать массив в список, правильно? Вы можете сделать это, используя определенный тип (TimePeriod вместо T) и следующий код.

Так что используйте что-то вроде этого:

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

Теперь вы можете передать список в любой метод, который хочет получить список.

Ответ 7

Если вы объявите фильтр как

public class Filter<T extends Iterable>

затем

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

И использование довольно просто:

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

Ответ 8

Ниже приведен пример класса оболочки вокруг Enum. Я немного странный bu - то, что мне нужно:

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

}

Ответ 9

Чтобы получить значение общего перечисления:

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

Обратите внимание, что в приведенном выше методе вызов метода toString().

И затем определите перечисление с помощью такого метода toString().

public enum MyNameEnum {

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

  private String name;

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

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

}