2012-04-12 2 views
27

Ich habe folgende Einstellungen:Aufzählungen gemeinsame statische Look-up-Methode

public enum MyEnum{ 
    A(10, "First"), // 
    B(20, "Second"), // 
    C(35, "Other options"); 

    private Integer code; 
    private String description; 

    private MyEnum(Integer code, String description) { 
     this.code = code; 
     this.description = description; 
    } 

    public Integer getCode() { 
     return code; 
    } 

    public String getDescription() { 
     return description; 
    } 

    public static MyEnum getValueOf(Integer code) { 
     for (MyEnum e : MyEnum.values()) { 
      if (e.getCode().equals(code)) { 
       return e; 
      } 
     } 
     throw new IllegalArgumentException("No enum const " + MyEnum.class.getName() + " for code \'" + code 
       + "\'"); 
    } 
} 

die gut arbeitet. Die getValueOf -Methode existiert, weil wir bei der Kommunikation mit einem externen Partner nur den Code (den sie ausgewählt haben) zuordnen. Ich brauche die Beschreibung, weil ich eine sinnvolle Phrase in der GUI zeigen muss.

Aber ich habe jetzt eine Reihe von ähnlichen Enum-Klassen. Alle haben ihren eigenen Code & Beschreibung, und sie benötigen die gleiche Lookup-Funktionalität. Ich wollte die getValueOf -Methode generisch sein, also brauche ich nicht 30+ verschiedene enums für im Grunde die gleiche Methode zu unterstützen.

Um dies zu lösen, wollte ich eine abstrakte Klasse erstellen, um diese Methode zu definieren und einige der allgemeinen Logik zu implementieren, aber das ist unmöglich, weil ich Enum nicht erweitern kann.

Dann habe ich versucht, eine Utility-Klasse mit der folgenden Methode zu machen:

public static <T extends Enum<T>> T getValueOf(Enum<T> type, Integer code) {...} 

Aber Generika mit Aufzählungen verwirrend ist, und ich verstehe nicht, wie diese Funktion zu erhalten.

Grundsätzlich möchte ich wissen: Was ist ein guter Ansatz, um ein gemeinsames Dienstprogramm zu einem Enum zu definieren?

Antwort

22

Sie müssen das tatsächliche Enum-Klassenobjekt als Parameter an Ihre Methode übergeben. Dann können Sie die Enum-Werte mit Class.getEnumConstants() abrufen.

Um in der Lage sein getCode() auf den Instanzen zugreifen zu können, sollten Sie es in einer Schnittstelle definieren und alle Ihre Enum-Typen, die Schnittstelle implementieren machen:

interface Encodable { 
    Integer getCode(); 
} 

public static <T extends Enum<T> & Encodable> T getValueOf(Class<T> enumClass, Integer code) { 
    for (T e : enumClass.getEnumConstants()) { 
     if (e.getCode().equals(code)) { 
      return e; 
     } 
    } 
    throw new IllegalArgumentException("No enum const " + enumClass.getName() + " for code \'" + code 
      + "\'"); 
} 

... 

public enum MyEnum implements Encodable { 
    ... 
} 

MyEnum e = getValueOf(MyEnum.class, 10); 
+0

FYI, das obige könnte eine NPE werfen, wenn getCode null zurückgibt. Ich würde die 'if'-Bedingung für null einchecken. –

+0

@JohnB, in der Tat. Ich habe den Code aus dem OP kopiert und mich nicht darum gekümmert, ihn zu verbessern. Wenn überhaupt, würde ich statt einer Nullprüfung den Typ von 'code' in' int' ändern, um das Problem im Keim zu ersticken. –

+1

Ich nahm das tatsächlich und in mein eigenes Dienstprogramm Projekt integriert. Made 'Encodable' generic so die Methode wurde' public static & Encodable > T getValueOf (Klasse enumClass, U-Code) 'Ich hoffe, es macht Ihnen nichts aus. –

7

Das ist ein bisschen schwierig ist, weil die values() Methode ist statisch. Sie müssten tatsächlich nachdenken, aber es gibt eine nette Möglichkeit, dies zu verbergen, indem Sie die Funktionen der Standardbibliothek verwenden.

Zuerst eine Schnittstelle mit den Methoden gemeinsam alle Ihre Enum-Typen definieren:

public interface Common { 
    Integer getCode(); 
    String getDescription(); 
} 

Dann machen Sie alle Ihre Aufzählungen diese Schnittstelle implementieren:

public enum MyEnum implements Common {...} 

Die statische Methode Sie wollen habe dann aussieht wie:

public static <T extends Enum<T> & Common> T getValueOf(Class<T> type, Integer code) { 
    final EnumSet<T> values = EnumSet.allOf(type); 
    for(T e : values) { 
     if(e.getCode().equals(code)) { 
      return e; 
     } 
    } 
    throw new IllegalArgumentException("No enum const " + type.getName() + " for code \'" + code + "\'"); 
} 
+0

geschlagen von 15 Sekunden, na ja.Die einzige Sache, die ich ändern würde, ist, 'getEnumConstants()' zu verwenden, anstatt ein EnumSet zu verursachen - das scheint ein wenig sinnlos. – Voo

+0

@Voo Ja, ziemlich die gleiche Antwort, aber ich dachte nicht, dass viele Leute es sofort wissen würden. ^^ Nun, EnumSets sind ziemlich effizient, der Overhead sollte niedrig sein, aber ich kannte diese Methode nicht um ehrlich zu sein. Ich sehe keinen Nachteil bei der Verwendung von 'getEnumConstants()', aber ich werde meine Antwort so lassen, wie sie ist, weil es noch eine andere Antwort gibt, die sie benutzt. –

+0

Dein Kommentar verwirrt mich. Siehst du eine Antwort von @Voo? Oder auf welche andere Antwort beziehen Sie sich? –