Итерация значений перечисления с использованием обобщенных java - PullRequest
62 голосов
/ 05 февраля 2010

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

Следующий код иллюстрирует то, что я хочу сделать. Обратите внимание, что код 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 () без аргументов. Это означает, что я не могу сделать selectedOption.getClass () без получения NPE. Этот класс фильтра управляет списком доступных опций, какая из опций выбрана. Если ничего не выбрано, selectedOption имеет значение 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);
        }
    }
}

Есть какие-нибудь идеи, как сделать это без необходимости использования дополнительного параметра Class в конструкторах?

Ответы [ 9 ]

102 голосов
/ 05 февраля 2010

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

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

public <T extends Enum<T>> void enumValues(Class<T> enumType) {
        for (T c : enumType.getEnumConstants()) {
             System.out.println(c.name());
        }
}
10 голосов
/ 05 февраля 2010

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

}
5 голосов
/ 22 сентября 2015

Для полноты 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(", " , "\"{", "}\""));
}

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

4 голосов
/ 05 февраля 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);
        }
    }
}
1 голос
/ 05 февраля 2010

Если вы уверены, что 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 : <b>this.selectedOption.getClass().getEnumConstants()</b>) {  // INVALID CODE
            availableOptions.add(option);
        }
    }
}

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

0 голосов
/ 08 декабря 2015

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

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

}
0 голосов
/ 07 марта 2014

Здесь ниже пример класса-оболочки вокруг Enum. Это немного странно, вот что мне нужно:

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 голосов
/ 05 февраля 2010

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

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());
        }
    }
}
0 голосов
/ 05 февраля 2010

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

Поэтому используйте что-то вроде этого:

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

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

...