Próbuję znaleźć sposób na iterację wartości wyliczenia podczas korzystania z typów ogólnych. Nie wiem, jak to zrobić lub czy jest to możliwe.
Poniższy kod ilustruje to, co chcę zrobić. Zauważ, że kod T.values () nie jest prawidłowy w poniższym kodzie.
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);
}
}
}
Oto, jak utworzę instancję obiektu Filter:
Filter<TimePeriod> filter = new Filter<TimePeriod>(TimePeriod.ALL);
Wyliczenie jest zdefiniowane w następujący sposób:
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;
}
}
Zdaję sobie sprawę, że kopiowanie wartości wyliczenia na listę może nie mieć sensu, ale używam biblioteki, która wymaga listy wartości jako danych wejściowych i nie będzie działać z wyliczeniami.
EDYCJA 05.02.2010:
Większość proponowanych odpowiedzi jest bardzo podobnych i sugeruje zrobienie czegoś takiego:
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);
}
}
}
Byłoby to świetnie, gdybym był pewien, że selectedOption ma wartość różną od null. Niestety, w moim przypadku użycia ta wartość jest często zerowa, ponieważ istnieje również publiczny konstruktor Filter () no-arg. Oznacza to, że nie mogę wykonać selectedOption.getClass () bez uzyskania NPE. Ta klasa filtru zarządza listą dostępnych opcji, które z nich są wybrane. Gdy nic nie jest zaznaczone, selectedOption ma wartość null.
Jedyną rzeczą, o której myślę, aby rozwiązać ten problem, jest przekazanie klasy w konstruktorze. Więc coś takiego:
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);
}
}
}
Jakieś pomysły, jak to zrobić bez konieczności dodatkowego parametru Class w konstruktorach?
źródło
getClass
Kod będzie również zawieść jeśli stała enum jest anonimowy podklasa ( przykład ).getDeclaringClass
należy użyć zamiast tego.Odpowiedzi:
To naprawdę trudny problem. Jedną z rzeczy, które musisz zrobić, jest poinformowanie javy, że używasz wyliczenia. Dzieje się tak przez stwierdzenie, że rozszerzasz klasę Enum dla swoich rodzajów. Jednak ta klasa nie ma funkcji values (). Musisz więc wziąć udział w zajęciach, dla których możesz uzyskać wartości.
Poniższy przykład powinien pomóc w rozwiązaniu problemu:
public <T extends Enum<T>> void enumValues(Class<T> enumType) { for (T c : enumType.getEnumConstants()) { System.out.println(c.name()); } }
źródło
getFriendlyName()
. Następnie musiałbyś dodać interfejs do swojego wyliczenia, a następnie dostosować powyższe typy tak, aby wymagały zarówno wyliczenia ORAZ interfejsu dla typuT
. Funkcja staje się wtedy następująca:public <E extends Enum<?> & EnumWithFriendlyName> void friendlyEnumValues(Class<T> enumType)
Inną opcją jest użycie 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); } }
źródło
Aby uzyskać kompletność, JDK8 daje nam względnie czysty i bardziej zwięzły sposób osiągnięcia tego bez potrzeby stosowania syntezy
values()
w klasie Enum:Biorąc pod uwagę proste wyliczenie:
private enum TestEnum { A, B, C }
I klient testowy:
@Test public void testAllValues() { System.out.println(collectAllEnumValues(TestEnum.class)); }
Spowoduje to wydrukowanie
{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(", " , "\"{", "}\"")); }
Kod można w prosty sposób dostosować do pobierania różnych elementów lub do zbierania w inny sposób.
źródło
Korzystanie z niebezpiecznej obsady:
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); } } }
źródło
getClass
zakończy się niepowodzeniem, jeśli stała wyliczeniowa jest anonimową podklasą ( przykład ).getDeclaringClass
należy użyć zamiast tego.Jeśli masz pewność, że
selectedOption
konstruktorFilter(T selectedOption)
nie jest pusty. Możesz użyć refleksji. Lubię to.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); } } }
Mam nadzieję że to pomoże.
źródło
Aby uzyskać wartość wyliczenia ogólnego:
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)); }
Zwróć uwagę na powyższą metodę wywołanie metody toString ().
A następnie zdefiniuj wyliczenie za pomocą takiej metody toString ().
public enum MyNameEnum { MR("John"), MRS("Anna"); private String name; private MyNameEnum(String name) { this.name = name; } public String toString() { return this.name; } }
źródło
Główny problem polega na tym, że musisz przekonwertować tablicę na listę, prawda? Możesz to zrobić, używając określonego typu (TimePeriod zamiast T) i następującego kodu.
Więc użyj czegoś takiego:
List<TimePeriod> list = new ArrayList<TimePeriod>(); list.addAll(Arrays.asList(sizes));
Teraz możesz przekazać listę do dowolnej metody, która potrzebuje listy.
źródło
Jeśli zadeklarujesz filtr jako
public class Filter<T extends Iterable>
następnie
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(); } }; } }
A użycie jest dość łatwe:
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()); } } }
źródło
Poniżej przykład klasy opakowującej wokół Enum. To trochę dziwne, ale jest to, czego potrzebuję:
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; }
}
źródło
Zrobiłem to w ten sposób
protected List<String> enumToList(Class<? extends Enum<?>> e) { Enum<?>[] enums = e.getEnumConstants(); return Arrays.asList(enums).stream() .map(name -> name.toString()) .collect(Collectors.toList()); }
źródło