Frage

Ich habe eine große Anzahl von Aufzählungen, die diese Schnittstelle implementieren:

/**
 * Interface for an enumeration, each element of which can be uniquely identified by it's 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();
}

Ein typisches Beispiel ist:

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

Wie Sie diese Methoden praktisch identisch vorstellen können in allen Implementierungen von CodableEnum sind. Ich möchte diese Doppelarbeit beseitigen, aber ehrlich gesagt nicht wissen, wie. Ich habe versucht, eine Klasse mit wie folgt:

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

Aber das erweist sich als ziemlich nutzlos zu sein, weil:

  1. Ein ENUM kann keine Klasse erweitern
  2. Elemente eines ENUM (SKYPE, GOOGLE_TALK, etc.) kann keine Klasse erweitern
  3. Ich kann keine Standardimplementierung von getByCode () zur Verfügung stellen, weil DefaultCodableEnum nicht selbst eine Enum. Ich habe versucht DefaultCodableEnum Wechsel java.lang.Enum zu verlängern, aber dies scheint nicht erlaubt zu werden.

Irgendwelche Vorschläge, die auf Reflexion nicht verlassen? Vielen Dank, Don

War es hilfreich?

Lösung

Sie können den duplizierten Code in eine CodeableEnumHelper Klasse Faktor:

public class CodeableEnumHelper {
    public static CodeableEnum getByCode(String code, CodeableEnum[] values) {
        for (CodeableEnum e : values) {
            if (e.getCode().equalsIgnoreCase(code)) {
                return e;
            }
        }
        return null;
    }
}

Jede CodeableEnum Klasse würde noch eine getByCode Methode implementieren müssen, aber die tatsächliche Durchführung des Verfahrens hat zumindest an einem Ort zentralisiert.

public enum IMType implements CodeableEnum {
    ...
    public IMType getByCode(String code) {
        return (IMType)CodeableEnumHelper.getByCode(code, this.values());
    } 
}

Andere Tipps

Abstrakte Aufzählungen sind potentiell sehr nützlich (und zur Zeit nicht erlaubt). Aber ein Vorschlag und Prototyp liegt vor, wenn Sie möchten, dass jemand es in Sun Lobby hinzuzufügen:

http://freddy33.blogspot.com/ 2007/11 / abstract-enum-ricky-carlson-way.html

Sun RFE:

http://bugs.sun.com/bugdatabase/view_bug.do? bug_id = 6570766

Um aufzuräumen Daves Code:

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

oder effizienter:

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

Ich hatte ein ähnliches Problem mit einem Lokalisierungskomponente, die ich geschrieben habe. Meine Komponente wurde entwickelt, lokalisierte Nachrichten mit Aufzählungskonstanten dieser Index in ein Ressourcenpaket zugreifen können, nicht ein schwieriges Problem.

Ich fand, dass ich das Kopieren und den gleichen „Vorlage“ Enum Code ganzen Orts einfügen. Meine Lösung, um die Vervielfältigung zu vermeiden, ist ein Code-Generator, der eine XML-Konfigurationsdatei mit dem Enum-Konstante-Namen und Konstruktor args akzeptiert. Die Ausgabe ist der Java-Quellcode mit den „dupliziert“ Verhaltensweisen.

Nun behaupte ich, die Konfigurationsdateien und den Generator, nicht alle der duplizierten Code. Überall habe ich Enum Quellcode hätte, gibt es nun eine XML-Konfigurationsdatei. Meine Build-Skripte erkennen out-of-date-Dateien generiert und rufen Sie den Code-Generator den ENUM-Code zu erstellen.

Sie können diese Komponente finden Sie unter hier . Die Vorlage, die ich Kopieren und Einfügen in ein XSLT-Stylesheet . Der Code Generator Sheet-Transformation ausgeführt wird. AN Eingangs Datei ist sehr prägnant im Vergleich zu dem erzeugten eNUM-Quellcode.

HTH,
Greg

Leider glaube ich nicht, dass es einen Weg gibt, dies zu tun. Ihre beste Wette wäre pro geschickt werden insgesamt in emums aufgeben und verwenden herkömmliche Klassenerweiterung und statische Mitglieder. Andernfalls, um diesen Code zu duplizieren gewöhnen. Es tut uns Leid.

Erstellen Sie eine typsichere Utility-Klasse, die Aufzählungen von Code geladen werden:

Die Schnittstelle kommt darauf an:

public interface CodeableEnum {
    String getCode();
}

Die Utility-Klasse ist:

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

}

Ein Testfall demonstriert Nutzung:

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

Jetzt sind Sie nur den getCode () -Methode zu duplizieren und das getByCode () -Methode an einem Ort ist. Es wäre schön, alle Ausnahmen in einer einzigen Runtime wickelt auch:)

Hier habe ich eine andere Lösung:

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

Der Trick besteht darin, dass die innere Klasse verwendet werden kann „globale Methoden“.

zu halten

funktionierte ziemlich gut für mich. OK, Sie haben drei Methoden zu implementieren, aber diese Methoden, sind nur delegators.

Es scheint, wie Sie tatsächlich Laufzeit Typinformationen Umsetzung sind. Java bietet diese als Sprache-Funktion.

Ich schlage vor, Sie schauen RTTI oder Reflexion.

Ich glaube nicht, das ist möglich. Sie können jedoch die valueOf (String name) -Methode des ENUM verwenden, wenn Sie den Enum-Wert den Namen als Code verwenden würden.

Wie sei es eine statische generische Methode? Man könnte es wieder verwendet aus Ihrem Enum der getByCode () Methoden oder einfach direkt verwenden. Ich Benutzer integer IDs für meine Aufzählungen immer, so mein getById () -Methode nur do zu tun hat dies: Rückgabewert () [id]. Es ist viel schneller und einfacher.

Wenn Sie wirklich Vererbung wollen, vergessen Sie nicht, dass Sie die Enum-Muster zu implementieren selbst , wie in den schlechten alten Java 1,4 Tagen.

Über so nah wie ich zu bekommen, was Sie wollen, war eine Vorlage in IntelliJ zu schaffen, die ‚Umsetzung‘ würde den generischen Code (mit Aufzählungs der valueOf (String name)). Nicht perfekt, aber funktioniert ganz gut.

In Ihrem speziellen Fall der getCode () / getByCode (String-Code) Methoden scheinen sehr geschlossen (euphemistisch gesprochen) auf das Verhalten des toString () / valueOf (String value) Methoden von alle Aufzählung zur Verfügung gestellt. Warum wollen Sie nicht, sie benutzen?

Eine andere Lösung wäre nichts in die Enum zu setzen selbst und bieten nur eine bidirektionale Karte Enum <-> Code-für jeden Enum. Sie könnten zum Beispiel verwenden ImmutableBiMap von Google-Kollektionen für diesen.

Auf diese Weise gibt es keinen doppelten Code überhaupt.

Beispiel:

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

Meiner Meinung nach, wäre dies der einfachste Weg sein, ohne Reflexion und ohne Zugabe von zusätzlichen Wrapper Ihrer Enum.

Sie erstellen eine Schnittstelle, die Ihre Enum implementiert:

public interface EnumWithId {

    public int getId();

}

Dann in einer Hilfsklasse erstellen Sie einfach eine Methode, wie diese:

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

Dieses Verfahren dann wie folgt verwendet werden:

MyUtil.getInstance().getById(MyEnum.class, myEnumId);
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top