2010-02-05 3 views
53

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

Следующий код иллюстрирует то, что я хочу сделать. Обратите внимание, что код 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<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; 
    } 
} 

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


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

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

+1

Параметр 'getClass' код также не будет, если константа перечисления является анонимным подкласс ([Пример] (https://ideone.com/ccjW7w)). ['getDeclaringClass'] (http://docs.oracle.com/javase/8/docs/api/java/lang/Enum.html#getDeclaringClass--). – Radiodef

ответ

85

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

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

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

Поскольку все эти ответы в основном одинаковы, я выбираю ваш, потому что он пришел первым. Это не решение дополнительного вопроса, когда я редактировал мой вопрос, но он позволил мне решить мои непосредственные потребности и двигаться дальше. Благодаря! – Tauren

+0

Не является ли c.name() именем элемента перечисления? Как насчет возврата его стоимости? Скажем, для пункта QUIZ («quizzie»), возвращающего «quizzie», а не QUIZ. – Stephane

+0

Для этого вам нужно будет иметь метод на перечислении, который возвращает значение: 'getFriendlyName()'. Затем вам нужно будет добавить интерфейс к вашему перечислению, а затем приспособить генерические элементы выше, чтобы потребовать как перечисление, так и интерфейс для типа 'T'. Функция затем становится чем-то вроде: 'public & EnumWithFriendlyName> void friendlyEnumValues ​​(класс enumType)' – Thirler

0

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

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

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

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

1

Если вы уверены, что 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); 
     } 
    } 
}

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

4

Использование небезопасных бросок:

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

'getClass' будет терпеть неудачу, если константа enum является анонимным подклассом ([пример] (https://ideone.com/ccjW7w)). ['getDeclaringClass'] (http://docs.oracle.com/javase/8/docs/api/java/lang/Enum.html#getDeclaringClass--). – Radiodef

7

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

} 
0

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

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

Интересный подход. Я думаю, что это сработало бы без объявления класса . Тем не менее, я бы предпочел не менять все мои перечисления, поэтому я склоняюсь к другому методу. – Tauren

0

Вот ниже пример класса обертки вокруг 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; 
} 

}

3

Для полноты JDK8 дает нам относительно чистый и более краткий способ достижения этой цели без необходимости использования synthethic 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(", " , "\"{", "}\"")); 
} 

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

0

Для того, чтобы получить значение родового перечисление:

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

} 
Смежные вопросы