enumの文字列表現をより単純にするために、すべて大文字のネーミングに反対してもかまいませんか?


14

たとえば、列挙型定数にタイトルケースまたはすべて小文字のネーミングを使用する人を見たことがあります。次に例を示します。

enum Color {
  red,
  yellow,
  green;
}

これによりthrow new IllegalStateException("Light should not be " + color + ".")、たとえば、必要に応じて、文字列形式での作業が簡単になります。

列挙型がの場合、これはより受け入れられるようですがprivate、それでも私はそれが好きではありません。次のように、Stringフィールドを使用してenumコンストラクタを作成し、toStringをオーバーライドしてその名前を返すことができることを知っています。

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

しかし、それがどれほど長いか見てください。シンプルにしたい小さな列挙型がたくさんある場合、これを続けるのは面倒です。ここでは、型破りなケースフォーマットを使用しても大丈夫ですか?


4
その慣習。慣習を破る理由はありますか?それはあなた次第です。

9
と言う例外メッセージで何が問題になっているのだろうと思っていますLight should not be RED.。結局のところ、このメッセージは開発者向けであり、ユーザーには表示されません。
ブランディン、2015年

1
@Brandin色が実装の詳細である場合、誰もそれを見るべきではありません。もちろん、それではなぜtoStringフォームが必要なのかという疑問が残ると思います。
コード

8
結局のところ、それはあなた次第です。コードをデバッグしていて、例外メッセージが表示されLight should not be YELLOW.た場合、それはある種の定数であると思いますが、toString()メソッドはデバッグ目的のためだけなので、そのメッセージでの表示を変更する必要がある理由がわかりません。
ブランディン、2015年

1
答えはあなたの「大丈夫」という考えに基づいていると思います。これを行えば、世界が爆発することはほとんどなく、プログラムを正常に作成することさえできます。役に立ちましたか?主観的すぎる。あなたが何らかの利益を得た場合、それはあなたにとってそれだけの価値がありますか?これらは私が気にかける質問です。
Garet Claborn、2015年

回答:


14

もちろん、短い答えは、基本的に定数であるものの命名規則を破るかどうかです... JLSからの引用:

定数名

インターフェース型の定数の名前は、クラス型の最終的な変数は、通常、1つ以上の単語、頭字語、または略語のシーケンスであり、コンポーネントはアンダースコア「_」文字で区切られています。定数名は、わかりやすく、不必要に省略しないでください。通常、それらは適切な品詞である場合があります。

の使用に関する長い答えは、値のtoString()より読みやすい表現が必要場合に、オーバーライドする方法であることは間違いありませんenumObject.toString()(強調鉱山)からの引用:

オブジェクトの文字列表現を返します。通常、このtoStringメソッドはこのオブジェクトを「テキストで表す」文字列を返します。結果は、簡潔でありながら有益な表現で、人が読みやすいものにする必要があります。すべてのサブクラスでこのメソッドをオーバーライドすることをお勧めします。

さて、なぜ値をenums使って前後に変換することについて話すことへの答えのいくつかがドリフトしたのかはわかりませんが、Stringここでも私の見解を説明します。このようなenum値のシリアル化は、name()またはordinal()メソッドを使用することで簡単に処理できます。両方ともそうなので、値の名前や位置が変更されない限りfinal、戻り値を確認できます。私には、それは十分に明らかなマーカーです。

上記から私が収集するのは、今日、YELLOW単に「黄色」と表現したいと思うかもしれません。明日は、「パントンミニオンイエロー」と表現したいと思うかもしれません。これらの記述は、呼び出しから返されるべきtoString()、と私はどちらか期待していないname()か、ordinal()変更します。そうした場合、それはコードベースまたはチーム内で解決する必要があることであり、単なるenumネーミングスタイルよりも大きな問題になります。

結論として、より読みやすいenum値の表現をログに記録することのみを目的としている場合でも、慣習に固執し、オーバーライドすることをお勧めしますtoString()。それらをデータファイルまたは他の非Java宛先にシリアル化するつもりである場合でも、バックアップするname()ordinal()メソッドがあるので、オーバーライドを心配する必要はありませんtoString()


5

変更しないでくださいENUM。これはコードの悪臭です。enumをenum、文字列を文字列とします。

代わりに、文字列値にはキャメルケースコンバーターを使用します。

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

Javaのこの問題をすでに解決している多くのオープンソースライブラリがあります。

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


特定のエッジケースでは非決定的ではないでしょうか?(特定のコンバーターの動作ではなく、一般的な原則)
Panzercrisis

-1基本的な文字列フォーマットに必要としないサードパーティのライブラリを追加するのはやり過ぎかもしれません。
user949300

1
@ user949300コードをコピーし、独自のコードを作成します。あなたは要点を逃しています。
Reactgular

「要点」について詳しく教えてください。「列挙型を列挙型、文字列を文字列にする」というのはいいように聞こえますが、それはどういう意味ですか?
user949300

1
Enumはタイプセーフを提供します。「じゃがいも」を色として通せない。そしておそらく、彼はRGB値などの列挙型に他のデータを含めますが、コードには表示されません。文字列の代わりに列挙型を使用する理由はたくさんあります。
user949300

3

Javaコードとデータベースまたはクライアントアプリの間で列挙型を送信すると、列挙型の値を文字列として読み書きすることがよくあります。 toString()文字列を連結するときに暗黙的に呼び出されます。一部の列挙型でtoString()をオーバーライドすると、時々私は単に

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

そして時々私は電話することを覚えなければなりませんでした

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

エラーが発生したので、もうそれはしません。実は、私はオーバーライドしない任意のあなたは十分なクライアントコードにそれらを周りに投げるならば、あなたは最終的に誰かの期待を破るだろうから列挙のメソッドを。

public String text()またはtoEnglish()などの独自の新しいメソッド名を作成します。

上記のような列挙型がたくさんある場合に、タイピングを節約できる小さなヘルパー関数を次に示します。

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

.toUpperCase()または.toLowerCase()を呼び出すのは常に簡単ですが、大文字と小文字を混在させることは難しい場合があります。「bleu de France」という色を考えてみましょう。フランスは常に大文字なので、それに遭遇した場合は、enumにtextLower()メソッドを追加することをお勧めします。このテキストを文の最初、文の途中、またはタイトルで使用すると、単一のtoString()メソッドでは不十分なことがわかります。また、Java識別子で違法な文字や、標準のキーボードでは表現されないために入力するのが面倒な文字や、大文字と小文字を区別しない文字(漢字など)にも触れません。

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

これらを適切に使用することはそれほど難しくありません:

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

うまくいけば、大文字と小文字が混在するenum値を使用すると失望する理由も示されます。アンダースコアの列挙型定数をすべて大文字にしておく最後の理由の1つは、最小の驚きの原則に従うことです。人々はそれを期待しているので、何か違うことをすると、常に自分自身を説明したり、コードを悪用する人々に対処したりする必要があります。


3
toString()enumをオーバーライドしないという提案は、私には意味がありません。列挙型名の保証されたデフォルトの動作が必要な場合は、を使用してくださいname()。にtoString()正しいキーを提供することに頼るのは、まったく「試みている」とは思えませんvalueOf(String)。あなたがあなたの列挙型をばかばかしいようにしたいなら、それは一つのことだと思いますが、それはあなたがオーバーライドしないことを勧める十分な理由ではないと思いますtoString()
codebreaker

1
さらに、私は列挙型でのtoStringをオーバーライドすることは実際には良いことだと(私は信じるように導かれたように)を推奨しており、これを見つけた:stackoverflow.com/questions/13291076/...
codebreaker

@codebreaker toString()は、文字列を連結するときに暗黙的に呼び出されます。一部の列挙型でtoString()をオーバーライドすることは、時々私がちょうどできることを意味し、時には<input type='checkbox' value=" + MY_CONST1 + ">呼び出すことを覚えなければならず<input type='checkbox' value=" + MY_CONST1.name() + ">、それがエラーにつながりました。
GlenPeterson、2015年

さて、それは良い点ですが、列挙型をその名前で "シリアル化"している場合にのみ問題になります(これは私の例で心配する必要のないことです)。
コード

0

これらの文字列表現がデータベースやテキストファイルなどの場所に格納される可能性が少しでもある場合、列挙型をリファクタリングする必要がある場合、それらを実際の列挙型定数に関連付けることは非常に問題になります。

「白」を含むデータファイルが存在Color.WhiteするColor.TitaniumWhite間に、ある日に名前を変更することにした場合はどうなりますか?

また、列挙型定数を文字列に変換し始めたら、次のステップは、ユーザーが表示できる文字列を生成することです。ここでの問題は、ご存じのとおり、Javaの構文では識別子内のスペースを許可します。(あなたが持つことは決してありませんColor.Titanium White。)したがって、おそらくユーザーに表示する列挙名を生成するための適切なメカニズムが必要になるため、不必要に列挙を複雑にすることを避けるのが最善です。

そうは言っても、もちろん先に進んで、自分が考えていることを実行してから、後で列挙型をリファクタリングして、問題が発生したときに(もしあれば)コンストラクタに名前を渡すことができます。

nameフィールドpublic finalを宣言してゲッターを失うことにより、enum-with-constructorから数行を削ることができます。(Javaプログラマーがゲッターに対して持っているこの愛は何ですか?)


public final staticは、それが由来するクラスへの参照ではなく、実際の定数として使用するコードにコンパイルされます。これにより、コードの部分的な再コンパイル(またはjarの置き換え)を行うときに、他の多くの楽しいエラーが発生する可能性があります。あなたが提案しpublic final static int white = 1;たり、public final static String white = "white";(再び慣習を破ることを除いて)、これは列挙型が提供する型安全性を失います。

@MichaelT Enumフィールドは静的ではありません。列挙型定数ごとにインスタンスが作成され、列挙型のメンバーはそのインスタンスのメンバー変数です。public finalまさに非最後のフィールドのようなコンストラクタ振る舞うから初期化されるインスタンスフィールドは、あなたが防止されていることを除いて、コンパイル時にそれに割り当てるから。リフレクションを介して変更でき、それを参照するすべてのコードはすぐに新しい値の表示を開始します。
Mike Nakis、2015年

0

間違いなく、大文字の命名規則に従うことはDRYに違反します。(そしてYAGNI)。たとえば、コード例#2では、「RED」と「red」が繰り返されています。

それで、あなたは公式大会に従うか、DRYに従うか?あなたの電話。

私のコードでは、DRYに従います。ただし、Enumクラスには「すべて大文字ではない(説明はこちら)」というコメントを付けます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.