Jak mogę wyeliminować zduplikowany Kod Enum?
Mam dużą liczbę Enum, które implementują ten interfejs:
/**
* Interface for an enumeration, each element of which can be uniquely identified by its code
*/
public interface CodableEnum {
/**
* Get the element with a particular code
* @param code
* @return
*/
public CodableEnum getByCode(String code);
/**
* Get the code that identifies an element of the enum
* @return
*/
public String getCode();
}
Typowym przykładem jest:
public enum IMType implements CodableEnum {
MSN_MESSENGER("msn_messenger"),
GOOGLE_TALK("google_talk"),
SKYPE("skype"),
YAHOO_MESSENGER("yahoo_messenger");
private final String code;
IMType (String code) {
this.code = code;
}
public String getCode() {
return code;
}
public IMType getByCode(String code) {
for (IMType e : IMType.values()) {
if (e.getCode().equalsIgnoreCase(code)) {
return e;
}
}
}
}
Jak można sobie wyobrazić te metody są praktycznie identyczne we wszystkich implementacjach CodableEnum. Chciałbym wyeliminować to powielanie, ale szczerze mówiąc Nie wiem jak. Próbowałem użyć klasy takiej jak poniżej:
public abstract class DefaultCodableEnum implements CodableEnum {
private final String code;
DefaultCodableEnum(String code) {
this.code = code;
}
public String getCode() {
return this.code;
}
public abstract CodableEnum getByCode(String code);
}
Ale okazuje się to dość bezużyteczne, ponieważ:
- enum nie może rozszerzyć klasy
- Elementy enum (SKYPE, GOOGLE_TALK, itp.) nie można rozszerzyć klasy
- nie mogę podać domyślnej implementacji getByCode (), ponieważ DefaultCodableEnum samo w sobie nie jest Enum. Próbowałem zmienić DefaultCodableEnum, aby rozszerzyć Javę.lang.Enum, ale to chyba nie jest dozwolone.
Jakieś sugestie, które nie opierają się na refleksji? Dzięki., Don
15 answers
Możesz dodać zduplikowany kod do klasy CodeableEnumHelper
:
public class CodeableEnumHelper {
public static CodeableEnum getByCode(String code, CodeableEnum[] values) {
for (CodeableEnum e : values) {
if (e.getCode().equalsIgnoreCase(code)) {
return e;
}
}
return null;
}
}
Każda klasa CodeableEnum
musiałaby zaimplementować metodę getByCode
, ale rzeczywista implementacja metody została scentralizowana przynajmniej w jednym miejscu.
public enum IMType implements CodeableEnum {
...
public IMType getByCode(String code) {
return (IMType)CodeableEnumHelper.getByCode(code, this.values());
}
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-01-30 02:51:53
Liczby abstrakcyjne są potencjalnie bardzo przydatne (i obecnie nie są dozwolone). Ale propozycja i prototyp istnieje, jeśli chcesz lobbować kogoś w Sun, aby go dodać:
Http://freddy33.blogspot.com/2007/11/abstract-enum-ricky-carlson-way.html
Sun RFE:
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-16 21:43:23
Uporządkować kod dave ' a:
public class CodeableEnumHelper {
public static <E extends CodeableEnum> E getByCode(
String code, E[] values
) {
for (E e : values) {
if (e.getCode().equalsIgnoreCase(code)) {
return e;
}
}
return null;
}
}
public enum IMType implements CodableEnum {
...
public IMType getByCode(String code) {
return CodeableEnumHelper.getByCode(code, values());
}
}
Lub bardziej efektywnie:
public class CodeableEnumHelper {
public static <E extends CodeableEnum> Map<String,E> mapByCode(
E[] values
) {
Map<String,E> map = new HashMap<String,E>();
for (E e : values) {
map.put(e.getCode().toLowerCase(Locale.ROOT), value) {
}
return map;
}
}
public enum IMType implements CodableEnum {
...
private static final Map<String,IMType> byCode =
CodeableEnumHelper.mapByCode(values());
public IMType getByCode(String code) {
return byCode.get(code.toLowerCase(Locale.ROOT));
}
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-10-11 01:06:15
Miałem podobny problem z komponentem lokalizacyjnym, który napisałem. Mój komponent jest przeznaczony do dostępu do zlokalizowanych wiadomości ze stałymi enum, które indeksują się w pakiecie zasobów, nie jest to trudny problem.
Odkryłem, że kopiuję i wklejam ten sam "szablon" kod enum wszędzie. Moim rozwiązaniem, aby uniknąć powielania jest generator kodu, który akceptuje plik konfiguracyjny XML z nazwami stałych enum i konstruktorem args. Wyjściem jest kod źródłowy Javy z zachowania "zduplikowane".
Teraz zachowuję pliki konfiguracyjne i generator, a nie cały zduplikowany kod. Wszędzie, gdzie miałbym kod źródłowy enum, jest teraz plik konfiguracyjny XML. Moje Skrypty kompilacji wykrywają nieaktualne wygenerowane pliki i wywołują generator kodu, aby utworzyć kod enum.
Możesz zobaczyć ten komponent tutaj . Szablon, który kopiowałem i wklejałem jest uwzględniany w arkuszu stylów XSLT. Kod generator uruchamia transformację arkusza stylów. Plik wejściowy jest dość zwięzły w porównaniu z wygenerowanym kodem źródłowym enum.
HTH,
Greg
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-17 16:02:59
Niestety, nie wydaje mi się, aby można było to zrobić. Najlepiej będzie, jeśli zrezygnujesz w emums i użyjesz konwencjonalnych rozszerzeń klasy i statycznych członków. W przeciwnym razie przywyknij do powielania tego kodu. Przepraszam.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-16 21:24:58
Utwórz klasę narzędzi bezpiecznych dla typu, która załaduje enums kodem:
Interfejs sprowadza się do:
public interface CodeableEnum {
String getCode();
}
Klasa użytkowa to:
import java.lang.reflect.InvocationTargetException;
public class CodeableEnumUtils {
@SuppressWarnings("unchecked")
public static <T extends CodeableEnum> T getByCode(String code, Class<T> enumClass) throws IllegalArgumentException, SecurityException, IllegalAccessException, InvocationTargetException, NoSuchMethodException {
T[] allValues = (T[]) enumClass.getMethod("values", new Class[0]).invoke(null, new Object[0]);
for (T value : allValues) {
if (value.getCode().equals(code)) {
return value;
}
}
return null;
}
}
Przypadek testowy demonstrujący użycie:
import junit.framework.TestCase;
public class CodeableEnumUtilsTest extends TestCase {
public void testWorks() throws Exception {
assertEquals(A.ONE, CodeableEnumUtils.getByCode("one", A.class));
assertEquals(null, CodeableEnumUtils.getByCode("blah", A.class));
}
enum A implements CodeableEnum {
ONE("one"), TWO("two"), THREE("three");
private String code;
private A(String code) {
this.code = code;
}
public String getCode() {
return code;
}
}
}
Teraz powielasz tylko metodę getCode (), a metoda getByCode () znajduje się w jednym miejscu. Może być też miło zawinąć wszystkie wyjątki w jednym RuntimeException:)
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-17 00:02:55
Tutaj mam inne rozwiązanie:
interface EnumTypeIF {
String getValue();
EnumTypeIF fromValue(final String theValue);
EnumTypeIF[] getValues();
class FromValue {
private FromValue() {
}
public static EnumTypeIF valueOf(final String theValue, EnumTypeIF theEnumClass) {
for (EnumTypeIF c : theEnumClass.getValues()) {
if (c.getValue().equals(theValue)) {
return c;
}
}
throw new IllegalArgumentException(theValue);
}
}
Sztuczka polega na tym, że Klasa wewnętrzna może być używana do przechowywania "metod globalnych".
Dla mnie zadziałało całkiem nieźle. OK, trzeba zaimplementować 3 metody, ale te metody, są tylko delegatorami.Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-11-26 21:09:20
Wygląda na to, że faktycznie implementujesz informacje typu run time. Java udostępnia to jako funkcję językową.
Proponuję sprawdzić RTTI lub reflection.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-16 21:28:28
To chyba niemożliwe. Możesz jednak użyć metody valueOf(String name) enum, jeśli chcesz użyć nazwy wartości enum jako kodu.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-16 21:33:20
A może statyczna metoda generyczna? Możesz użyć go ponownie z poziomu metod getByCode() lub po prostu użyć go bezpośrednio. Ja zawsze user integer ID dla moich enum, więc moja metoda getById () ma tylko do do this: return values () [id]. Jest o wiele szybszy i prostszy.
Jeśli naprawdę chcesz dziedziczyć, nie zapominaj, że możesz zaimplementować wzór enum samodzielnie, jak w starej, złej Javie 1.4 days.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-16 21:53:48
Mniej więcej tak blisko, jak doszedłem do tego, czego chcesz, było stworzenie szablonu w IntelliJ, który "zaimplementowałby" kod generyczny(używając wartości enum (String name)). Nie idealne, ale działa całkiem dobrze.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-16 22:39:10
W twoim konkretnym przypadku, metody getCode() / getByCode(String code) wydają się bardzo zamknięte (eufemistycznie mówiąc) na zachowanie metod toString() / valueOf(String value) dostarczanych przez wszystkie wyliczenia. Dlaczego nie chcesz ich użyć?
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-17 11:00:54
Innym rozwiązaniem byłoby nie umieszczanie niczego w samym enum, a po prostu dostarczenie dwukierunkowego kodu mapy Enum dla każdego enum. Możesz np. użyć ImmutableBiMap z kolekcji Google.
W ten sposób nie ma duplikatu kodu w ogóle.
Przykład:
public enum MYENUM{
VAL1,VAL2,VAL3;
}
/** Map MYENUM to its ID */
public static final ImmutableBiMap<MYENUM, Integer> MYENUM_TO_ID =
new ImmutableBiMap.Builder<MYENUM, Integer>().
put(MYENUM.VAL1, 1).
put(MYENUM.VAL2, 2).
put(MYENUM.VAL3, 3).
build();
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-03-18 17:37:33
Moim zdaniem byłby to najprostszy sposób, bez refleksji i bez dodawania dodatkowego opakowania do twojego enum.
Tworzysz interfejs, który implementuje Twój enum:
public interface EnumWithId {
public int getId();
}
Następnie w klasie pomocniczej tworzysz metodę taką jak Ta:
public <T extends EnumWithId> T getById(Class<T> enumClass, int id) {
T[] values = enumClass.getEnumConstants();
if (values != null) {
for (T enumConst : values) {
if (enumConst.getId() == id) {
return enumConst;
}
}
}
return null;
}
Metoda ta może być użyta w następujący sposób:
MyUtil.getInstance().getById(MyEnum.class, myEnumId);
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-05-19 15:42:13