Überschreiben Sie valueof () und toString () in der Java-Enumeration

122

Die Werte in my enumsind Wörter, die Leerzeichen enthalten müssen, aber Enums können keine Leerzeichen in ihren Werten enthalten, sodass alles zusammengefasst ist. Ich möchte überschreiben toString(), um diese Leerzeichen dort hinzuzufügen, wo ich es sage.

Ich möchte auch, dass die Aufzählung die richtige Aufzählung liefert, wenn ich valueOf()dieselbe Zeichenfolge verwende, zu der ich die Leerzeichen hinzugefügt habe.

Beispielsweise:

public enum RandomEnum
{
     StartHere,
     StopHere
}

Rufen Sie toString()an , RandomEnumdessen Wert StartHerezurückkehrt Zeichenfolge "Start Here". Der Aufruf valueof()derselben Zeichenfolge ( "Start Here") gibt den Aufzählungswert zurück StartHere.

Wie kann ich das machen?

WildBamaBoy
quelle
2
Fügen Sie das "Java" -Tag hinzu, um weitere Kommentare / Antworten zu erhalten.
Java42
Mögliches Duplikat der Implementierung von toString auf Java-Enums
Steve Chambers

Antworten:

197

Sie können diesen Code ausprobieren. Da Sie die valueOfMethode nicht überschreiben können , müssen Sie eine benutzerdefinierte Methode definieren ( getEnumim folgenden Beispielcode), die den benötigten Wert zurückgibt, und Ihren Client so ändern, dass stattdessen diese Methode verwendet wird.

public enum RandomEnum {

    StartHere("Start Here"),
    StopHere("Stop Here");

    private String value;

    RandomEnum(String value) {
        this.value = value;
    }

    public String getValue() {
        return value;
    }

    @Override
    public String toString() {
        return this.getValue();
    }

    public static RandomEnum getEnum(String value) {
        for(RandomEnum v : values())
            if(v.getValue().equalsIgnoreCase(value)) return v;
        throw new IllegalArgumentException();
    }
}
Jugal Shah
quelle
4
getEnum kann verkürzt werden, wenn Sie Folgendes tun: v.getValue (). equals (value); Die Nullprüfung kann weggelassen werden.
Rtcarlson
Sie können eine Karte auch träge erstellen und wiederverwenden. Achten Sie jedoch beim Erstellen auf Threading-Probleme.
Maarten Bodewes
11
funktioniert nicht in Szenarien, in denen Sie den Aufruf der Methode valueOf () nicht in getValue () ändern können, z. B. wenn Sie bestimmte Felder über Annotationen im
Ruhezustand
Bis Enums in Java behoben sind, bietet @Bat eine geeignetere und OO-freundlichere Lösung. name () sollte nicht endgültig sein.
Andrew T Finnell
anstelle von for können Sie valueOf (RandomEnum.class, value) verwenden.
Wojtek
22

Versuchen Sie dies, aber ich bin nicht sicher, ob das überall funktionieren wird :)

public enum MyEnum {
    A("Start There"),
    B("Start Here");

    MyEnum(String name) {
        try {
            Field fieldName = getClass().getSuperclass().getDeclaredField("name");
            fieldName.setAccessible(true);
            fieldName.set(this, name);
            fieldName.setAccessible(false);
        } catch (Exception e) {}
    }
}
Schläger
quelle
18
Warum bin ich der einzige, für den solche Sachen böse aussehen? Ich meine, es sieht wirklich cool aus, aber jemand ohne Kontext, der diesen Code liest, wäre höchstwahrscheinlich wie "WTF?".
Nicolas Guillaume
11
@NicolasGuillaume Das ist die Art von Code, der bei seiner Implementierung dringend einen Kommentar benötigt, der erklärt, warum er vorhanden ist und welches Problem der Programmierer zu lösen versuchte. :-)
Ti Strga
9
Fangen Sie keine generische Ausnahme, da dies OutOfMemory oder irgendetwas sein könnte
Crusy
2
Das ist eine schreckliche Idee. Nicht zuletzt besteht die Möglichkeit eines stillen Fehlers ohne Anzeige oder Wiederherstellung. Auch jetzt unterscheiden sich der "Name" -Wert und der Symbolwert im Code (z. B. A, B). +2 für klug sein. -200 für schrecklich schlau. Auf keinen Fall würde ich dies jemals als Codeüberprüfung genehmigen.
Pedorro
1
@pedorro Das scheint nicht so schrecklich zu sein, wie du es dir vorstellst. Jeder, der verstanden hat, dass alles, was das Java-Komitee tut, nicht als Evangelium verstanden werden sollte, würde dies in einer Code-Überprüfung bestehen. Es ist unendlich schlimmer, alle Enum-Dienstprogrammmethoden neu schreiben und dann beibehalten zu müssen, da name () nicht überschrieben werden kann. Anstatt die Ausnahme abzufangen und zu verschlucken, sollte eine RuntimeException ausgelöst werden, und dann ist dies in Ordnung.
Andrew T Finnell
14

Sie können in Ihrer Aufzählung eine statische Map verwenden, die Strings Enum-Konstanten zuordnet. Verwenden Sie es in einer statischen Methode 'getEnum'. Dadurch wird die Notwendigkeit übersprungen, die Aufzählungen jedes Mal zu durchlaufen, wenn Sie eine von ihrem String-Wert erhalten möchten.

public enum RandomEnum {

    StartHere("Start Here"),
    StopHere("Stop Here");

    private final String strVal;
    private RandomEnum(String strVal) {
        this.strVal = strVal;
    }

    public static RandomEnum getEnum(String strVal) {
        if(!strValMap.containsKey(strVal)) {
            throw new IllegalArgumentException("Unknown String Value: " + strVal);
        }
        return strValMap.get(strVal);
    }

    private static final Map<String, RandomEnum> strValMap;
    static {
        final Map<String, RandomEnum> tmpMap = Maps.newHashMap();
        for(final RandomEnum en : RandomEnum.values()) {
            tmpMap.put(en.strVal, en);
        }
        strValMap = ImmutableMap.copyOf(tmpMap);
    }

    @Override
    public String toString() {
        return strVal;
    }
}

Stellen Sie einfach sicher, dass die statische Initialisierung der Karte unterhalb der Deklaration der Enum-Konstanten erfolgt.

Übrigens - dieser 'ImmutableMap'-Typ stammt aus der Google Guava-API, und ich empfehle ihn auf jeden Fall in solchen Fällen.


BEARBEITEN - Gemäß den Kommentaren:

  1. Diese Lösung setzt voraus, dass jeder zugewiesene Zeichenfolgenwert eindeutig und nicht null ist. Angesichts der Tatsache, dass der Ersteller der Aufzählung dies steuern kann und die Zeichenfolge dem eindeutigen und nicht null Aufzählungswert entspricht, scheint dies eine sichere Einschränkung zu sein.
  2. Ich habe die in der Frage angeforderte Methode 'toSTring ()' hinzugefügt
pedorro
quelle
1
Ich habe ein paar Abstimmungen zu dieser Antwort erhalten - möchte jemand näher erläutern, warum diese Antwort schlecht ist? Ich bin nur neugierig, was die Leute denken.
Pedorro
Dies schlägt fehl für ein Szenario mit mehreren Enum-Konstanten mit demselben 'Wert' wie RestartHere("replay"), ReplayHere("replay")oder ohne 'Wert' wie PauseHere, WaitHere(dh Enum hat einen Standardkonstruktor wieprivate RandomEnum() { this.strVal = null; }
sactiw
1
Sie sollten ImmutableMap.Builder verwenden, anstatt eine MutableMap zum Erstellen von + ImmutableMap :: copyOf zu erstellen.
Bryan Correll
Das sieht interessant aus, aber wenn die Aufzählung nur wenige unterschiedliche Werte hat, wird es nicht lange dauern, sie zu durchlaufen. Es lohnt sich wahrscheinlich nur, wenn die Aufzählung viele Werte hat.
Ben Thurley
13

Wie wäre es mit einer Java 8-Implementierung? (null kann durch Ihre Standardaufzählung ersetzt werden)

public static RandomEnum getEnum(String value) {
    List<RandomEnum> list = Arrays.asList(RandomEnum.values());
    return list.stream().filter(m -> m.value.equals(value)).findAny().orElse(null);
}

Oder Sie könnten verwenden:

...findAny().orElseThrow(NotFoundException::new);
corlaez
quelle
2

Ich glaube nicht, dass Sie valueOf ("Start Here") zum Laufen bringen werden. Aber was die Leerzeichen betrifft ... versuchen Sie Folgendes ...

static private enum RandomEnum {
    R("Start There"), 
    G("Start Here"); 
    String value;
    RandomEnum(String s) {
        value = s;
    }
}

System.out.println(RandomEnum.G.value);
System.out.println(RandomEnum.valueOf("G").value);

Start Here
Start Here
Java42
quelle
2

Das Folgende ist eine nette generische Alternative zu valueOf ()

public static RandomEnum getEnum(String value) {
  for (RandomEnum re : RandomEnum.values()) {
    if (re.description.compareTo(value) == 0) {
      return re;
    }
  }
  throw new IllegalArgumentException("Invalid RandomEnum value: " + value);
}
Ausnahme
quelle
ist die Verwendung compareTo()eleganter als equals()für Streicher?
Betlista
Eleganz ist kein Thema - ich stimme zu, dass .equals()das gut funktionieren würde. Könnte verwenden, ==wenn Sie möchten. (Obwohl es ein guter Grund ist, dies nicht zu
Ausnahme
3
@exception NIEMALS == für String-Vergleiche verwenden, da dies die Objektgleichheitsprüfung durchführt, während Sie eine Inhaltsgleichheitsprüfung benötigen und dafür die equals () -Methode der String-Klasse verwenden.
Sactiw
2

Sie haben noch die Möglichkeit, Folgendes in Ihre Aufzählung zu implementieren:

public static <T extends Enum<T>> T valueOf(Class<T> enumType, String name){...}
Andrey Lebedenko
quelle