Ist es in Ordnung, gegen All-Caps-Namen für Enums vorzugehen, um deren String-Darstellung zu vereinfachen?

14

Ich habe mehrmals gesehen, wie Leute Titel- oder sogar Kleinbuchstaben für Enum-Konstanten verwenden, zum Beispiel:

enum Color {
  red,
  yellow,
  green;
}

Dies macht das Arbeiten mit ihrer Zeichenfolgenform einfach und leicht, wenn Sie dies throw new IllegalStateException("Light should not be " + color + ".")beispielsweise tun möchten .

Dies scheint akzeptabler zu sein, wenn die Aufzählung ist private, aber ich mag es immer noch nicht. Ich weiß, dass ich einen Enum-Konstruktor mit einem String-Feld erstellen und dann toString überschreiben kann, um diesen Namen wie folgt zurückzugeben:

enum Color {
  RED("red"),
  YELLOW("yellow"),
  GREEN("green");

  private final String name;

  private Color(String name) { 
    this.name = name 
  }

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

Aber schauen Sie, wie lange das noch dauert. Es ist ärgerlich, weiterzumachen, wenn Sie ein paar kleine Aufzählungen haben, die Sie einfach halten möchten. Ist es in Ordnung, hier nur unkonventionelle Fallformate zu verwenden?

Codeknacker
quelle
4
Es ist eine Konvention. Haben Sie Grund, die Konvention zu brechen? Das liegt an dir.
9
Ich frage mich nur, was mit einer Ausnahmemeldung, die besagt, falsch ist Light should not be RED.. Schließlich ist diese Nachricht für den Entwickler bestimmt, der Benutzer sollte sie niemals sehen.
Brandin
1
@Brandin Wenn die Farbe eher ein Implementierungsdetail ist, sollte sie niemand sehen. Dann bleibt natürlich die Frage, warum wir ein nettes toString-Formular brauchen.
Codebrecher
8
Am Ende des Tages liegt es an Ihnen. Wenn ich Ihren Code debuggte und eine Ausnahmemeldung sah, Light should not be YELLOW.würde ich vermuten, dass es sich um eine Konstante handelt. Die toString () -Methode dient nur zu Debugging-Zwecken, sodass ich nicht verstehe, warum Sie das Aussehen in dieser Nachricht ändern müssen.
Brandin
1
Ich denke, die Antwort basiert mehr auf Ihrer Vorstellung von "okay". Die Welt wird höchstwahrscheinlich nicht in Flammen aufgehen, wenn Sie dies tun und sogar erfolgreich ein Programm schreiben können. Ist es nützlich? meh zu subjektiv. Wenn Sie einen Nutzen daraus ziehen, lohnt es sich für Sie und wird es andere betreffen? Das sind die Fragen, die mir wichtig sind.
Garet Claborn

Antworten:

14

Die kurze Antwort lautet natürlich, ob Sie mit Namenskonventionen für im Wesentlichen Konstanten brechen möchten ... Zitat aus dem JLS :

Konstante Namen

Die Namen von Konstanten in Schnittstellentypen sollten und Endvariablen von Klassentypen üblicherweise eine Folge von einem oder mehreren Wörtern, Akronymen oder Abkürzungen sein, alle in Großbuchstaben, wobei die Komponenten durch Unterstriche "_" getrennt sind. Konstante Namen sollten beschreibend sein und nicht unnötig abgekürzt werden. Herkömmlicherweise können sie ein geeigneter Teil der Rede sein.

Die lange Antwort in Bezug auf die Verwendung von toString()ist, dass dies definitiv die Methode ist, die überschrieben werden muss, wenn Sie eine besser lesbare Darstellung der enumWerte wünschen . Zitat aus Object.toString()(Hervorhebung von mir):

Gibt eine Zeichenfolgendarstellung des Objekts zurück. Im Allgemeinen gibt die toStringMethode eine Zeichenfolge zurück, die dieses Objekt "textuell darstellt" . Das Ergebnis sollte eine präzise, ​​aber informative Darstellung sein , die für eine Person leicht zu lesen ist . Es wird empfohlen, dass alle Unterklassen diese Methode überschreiben.

Jetzt bin ich mir nicht sicher, warum einige der Antworten darauf gerichtet waren, über das Konvertieren enumsmit StringWerten hin und her zu sprechen , aber ich werde auch hier nur meine Meinung dazu abgeben . Eine solche Serialisierung von enumWerten kann leicht mit den Methoden name()oder durchgeführt ordinal()werden. Beides ist finalund somit können Sie sich der zurückgegebenen Werte sicher sein, solange sich die Namen oder die Position der Werte nicht ändern . Für mich ist das ein klarer Marker.

Was ich aus dem Obigen zusammenfasse, ist: Heute möchten Sie es vielleicht YELLOWeinfach als "gelb" beschreiben. Morgen möchten Sie es vielleicht als "Pantone Minion Yellow" beschreiben . Diese Beschreibungen sollten vom Anruf zurückgegeben werden toString(), und ich würde weder erwarten name()noch ordinal()ändern. Wenn ich das tue, muss ich das in meiner Codebasis oder in meinem Team lösen und es wird zu einer größeren Frage als nur zu einem enumNamensstil .

Wenn Sie lediglich eine besser lesbare Darstellung Ihrer enumWerte protokollieren möchten, empfehle ich dennoch, sich an die Konventionen zu halten und diese dann zu überschreiben toString(). Wenn Sie sie auch in eine Datendatei oder in andere Nicht-Java-Ziele serialisieren möchten, verfügen Sie weiterhin über die Methoden name()und ordinal(), um Sie zu sichern, sodass Sie sich nicht über das Überschreiben ärgern müssen toString().

hjk
quelle
5

Ändern Sie nicht, ENUMdass dies nur ein schlechter Codegeruch ist. Sei eine Aufzählung eine Aufzählung und eine Zeichenfolge eine Zeichenfolge.

Verwenden Sie stattdessen einen CamelCase-Konverter für Zeichenfolgenwerte.

throw new IllegalStateException("Light should not be " + CamelCase(color) + ".");

Es gibt viele Open Source-Bibliotheken, die dieses Problem bereits für Java lösen.

http://docs.guava-libraries.googlecode.com/git/javadoc/com/google/common/base/CaseFormat.html

Reactgular
quelle
Wäre es nicht in bestimmten Randfällen nicht deterministisch? (Nicht das Verhalten eines bestimmten Konverters, sondern das allgemeine Prinzip)
Panzercrisis
-1 Das Hinzufügen einer Bibliothek eines Drittanbieters, die möglicherweise nicht den Anforderungen für die grundlegende Formatierung von Zeichenfolgen entspricht, ist möglicherweise übertrieben.
user949300
1
@ user949300 kopiere den Code, schreibe deinen eigenen oder was auch immer. Sie verpassen den Punkt.
Reactgular
Können Sie "den Punkt" näher erläutern? "Eine Aufzählung sei eine Aufzählung und eine Zeichenfolge eine Zeichenfolge" klingt gut, aber was bedeutet das wirklich?
user949300
1
Enum bietet Typensicherheit. Er kann "Kartoffel" nicht als Farbe übergeben. Und vielleicht nimmt er andere Daten in die Aufzählung auf, wie z. B. den RGB-Wert, der im Code einfach nicht angezeigt wird. Es gibt viele gute Gründe, eine Aufzählung anstelle einer Zeichenfolge zu verwenden.
user949300
3

Wenn ich Enums zwischen meinem Java-Code und einer Datenbank oder Client-App sende, lese und schreibe ich die Enum-Werte häufig als Zeichenfolgen. toString()wird implizit aufgerufen, wenn Zeichenfolgen verkettet werden. Das Überschreiben von toString () in einigen Aufzählungen bedeutete, dass ich es manchmal einfach konnte

"<input type='checkbox' value='" + MY_CONST1 + "'>"

und manchmal musste ich daran denken anzurufen

"<input type='checkbox' value='" + MY_CONST1.name() + "'>"

was zu Fehlern führte, also mache ich das nicht mehr. Eigentlich überschreibe ich keine Methoden in Enum, denn wenn Sie sie in genügend Client-Code umwandeln, werden Sie irgendwann die Erwartungen von jemandem brechen.

Machen Sie Ihren eigenen neuen Methodennamen, wie public String text()oder toEnglish()oder was auch immer.

Hier ist eine kleine Hilfsfunktion, die Ihnen das Tippen ersparen könnte, wenn Sie viele Aufzählungen wie die oben genannten haben:

public static String ucFirstLowerRest(String s) {
    if ( (s == null) || (s.length() < 1) ) {
        return s;
    } else if (s.length() == 1) {
        return s.toUpperCase();
    } else {
        return s.substring(0, 1).toUpperCase() + s.substring(1).toLowerCase();
    }
}

Es ist immer einfach, .toUpperCase () oder .toLowerCase () aufzurufen, aber es kann schwierig sein, gemischte Groß- und Kleinschreibung zurückzugewinnen. Betrachten Sie die Farbe "bleu de France". Frankreich wird immer groß geschrieben, daher möchten Sie möglicherweise eine textLower () -Methode zu Ihrer Aufzählung hinzufügen, wenn Sie darauf stoßen. Wenn Sie diesen Text am Anfang eines Satzes oder in der Mitte eines Satzes oder in einem Titel verwenden, können Sie sehen, wie eine einzelne toString()Methode zu kurz kommt. Und das berührt nicht einmal Zeichen, die in Java-Bezeichnern illegal sind oder deren Eingabe schwierig ist, weil sie nicht auf Standardtastaturen dargestellt werden, oder Zeichen, die keine Groß- / Kleinschreibung haben (Kanji usw.).

enum Color {
  BLEU_DE_FRANCE {
    @Override public String textTc() { return "Bleu De France"; }
    @Override public String textLc() { return "bleu de France"; }
  }
  CAFE_NOIR {
    @Override public String textTc() { return "Café Noir"; }
  }
  RED,
  YELLOW,
  GREEN;

  // The text in title case
  private final String textTc;

  private Color() { 
    textTc = ucFirstLowerRest(this.toString());
  }

  // Title case
  public String textTc() { return textTc; }

  // For the middle of a sentence
  public String textLc() { return textTc().toLowerCase(); }

  // For the start of a sentence
  public String textUcFirst() {
    String lc = textLc();
    return lc.substring(0, 1).toUpperCase() + lc.substring(1);
  }
}

Es ist nicht so schwierig, diese richtig zu verwenden:

IllegalStateException(color1.textUcFirst() + " clashes horribly with " +
                      color2.textLc() + "!")

Hoffentlich zeigt dies auch, warum die Verwendung von Enum-Werten in gemischten Groß- und Kleinschreibung Sie ebenfalls enttäuschen wird. Ein letzter Grund, bei All-Caps mit Unterstreichungs-Enum-Konstanten zu bleiben, ist, dass dies dem Prinzip des geringsten Erstaunens folgt. Die Leute erwarten es. Wenn Sie also etwas anderes tun, müssen Sie sich immer selbst erklären oder mit Leuten umgehen, die Ihren Code missbrauchen.

GlenPeterson
quelle
3
Der Vorschlag, toString()eine Aufzählung niemals außer Kraft zu setzen, macht für mich keinen Sinn. Wenn Sie das garantierte Standardverhalten von Aufzählungsnamen möchten, verwenden Sie name(). Ich finde es überhaupt nicht "verlockend", sich darauf toString()zu verlassen, den richtigen Schlüssel für zu liefern valueOf(String). Ich denke, wenn Sie Ihre Aufzählungen idiotensicher machen wollen, ist das eine Sache, aber ich denke nicht, dass dies ein guter Grund ist, zu empfehlen, dass Sie niemals überschreiben sollten toString().
Codebreaker
1
Darüber hinaus fand ich dies, was empfiehlt (wie ich zu der Annahme gebracht wurde), dass das Überschreiben von String in Aufzählungen tatsächlich eine gute Sache ist: stackoverflow.com/questions/13291076/…
Codebreaker
@codebreaker toString () wird implizit aufgerufen, wenn Zeichenfolgen verkettet werden. Das Überschreiben von toString () in einigen Aufzählungen bedeutete, dass ich manchmal einfach <input type='checkbox' value=" + MY_CONST1 + ">konnte und manchmal daran denken musste, aufzurufen <input type='checkbox' value=" + MY_CONST1.name() + ">, was zu Fehlern führte.
GlenPeterson
Okay, das ist ein guter Punkt, aber es ist nur dann wirklich wichtig, wenn Sie Enums mit ihrem Namen "serialisieren" (worüber ich mich in meinem Beispiel nicht Gedanken machen muss).
Codebrecher
0

Wenn die geringste Wahrscheinlichkeit besteht, dass diese Zeichenfolgendarstellungen an Orten wie Datenbanken oder Textdateien gespeichert werden, ist es äußerst problematisch, sie an die tatsächlichen Aufzählungskonstanten zu binden, wenn Sie Ihre Aufzählung jemals umgestalten müssen.

Was passiert , wenn Sie einen Tag umbenennen entscheiden Color.Whitezu , Color.TitaniumWhitewährend es Datendateien da draußen „White“ enthalten?

Sobald Sie mit der Konvertierung von Enum-Konstanten in Zeichenfolgen beginnen, besteht der nächste Schritt darin, Zeichenfolgen zu generieren, die der Benutzer sehen kann. Das Problem hier ist, wie Sie sicher bereits wissen, dass die Syntax von Java dies nicht tut Leerzeichen innerhalb von Bezeichnern zulassen. (Das werden Sie nie haben Color.Titanium White.) Da Sie wahrscheinlich einen geeigneten Mechanismus zum Generieren von Aufzählungsnamen benötigen, der dem Benutzer angezeigt wird, sollten Sie vermeiden, Ihre Aufzählung unnötig zu komplizieren.

Nachdem dies gesagt wurde, können Sie natürlich weitermachen und das tun, woran Sie gedacht haben, und später Ihre Aufzählung umgestalten, um dem Konstruktor Namen zu geben, wenn (und wenn) Sie in Schwierigkeiten geraten.

Sie können ein paar Zeilen von Ihrem Enum-with-Konstruktor rasieren, indem Sie das nameFeld deklarieren public finalund den Getter verlieren. (Was ist diese Liebe, die Java-Programmierer zu Gettern haben?)

Mike Nakis
quelle
Eine öffentliche endgültige Statik wird in Code kompiliert, der sie als tatsächliche Konstante verwendet und nicht als Verweis auf die Klasse, aus der sie stammt. Dies kann eine Reihe anderer lustiger Fehler verursachen, wenn Code teilweise neu kompiliert wird (oder ein JAR ersetzt wird). Wenn Sie vorschlagen public final static int white = 1;oder public final static String white = "white";(abgesehen von einem erneuten Verstoß gegen die Konvention), verliert dies die Typensicherheit, die die Aufzählung bietet.
@ MichaelT Enum-Felder sind nicht statisch. Für jede Enum-Konstante wird eine Instanz erstellt, und die Mitglieder der Enum sind Mitgliedsvariablen dieser Instanz. Ein public finalInstanzfeld, das vom Konstruktor initialisiert wird, verhält sich genau wie ein nicht endgültiges Feld, außer dass Sie beim Kompilieren daran gehindert werden , es zuzuweisen. Sie können es über Reflection ändern, und der gesamte Code, der darauf verweist, zeigt sofort den neuen Wert an.
Mike Nakis
0

Das Befolgen der UPPERCASE-Namenskonvention verstößt möglicherweise gegen DRY . (Und YAGNI ). Beispiel: In Ihrem Codebeispiel 2 werden "ROT" und "Rot" wiederholt.

Befolgen Sie also die offizielle Konvention oder DRY? Ihr Anruf.

In meinem Code werde ich DRY folgen. Ich werde jedoch einen Kommentar zur Enum-Klasse abgeben und sagen: "Nicht alle Großbuchstaben, weil (Erklärung hier)"

user949300
quelle