Java 7で文字列をオンに切り替えることの利点は何ですか?


19

Javaでプログラミングを始めたとき、switchステートメントが文字列を受け取らないという事実にイライラしました。その後、列挙型を使用すると、型の安全性(リファクタリングが容易になる)と他の開発者にとってわかりやすい値を渡すのではなく、列挙型を使用するメリットを実感しました。

SE7では、Enumではなく、文字列を入力として使用するスイッチを使用することにした状況を考えるのに苦労しています。文字列全体を切り替えることで実装されている場合(たとえば、部分一致または正規表現の一致ではなく)、コードが変更される理由が少なくなるようには見えません。

また、IDEツールとコーディングの読み取り/書き込み比率により、文字列値を渡すよりも余分なEnumを自動生成する方がはるかに幸せです。

彼らはプログラマーとしてどのような利益をもたらしますか?ボイラープレートが少ない?

この機能のために言語が泣き叫ぶような気はしません。たぶん私は見落としているユースケースがあります。


文字列は既にswitchステートメントで多く使用されていますが、人々はそれらを直接使用できないため、巨大なif elseツリーや列挙型への変換などの回避策に頼りました。両方の回避策により、コードが読みにくくなります。どちらの方法も回避策であり、ネイティブに実装されたソリューションがプログラマの意図をよりよく示している場合に回避策を使用する理由です。
ピーターB

@PieterBおそらく、Enumは回避策ではなくセマンティック値を追加しているため、タイプセーフがもたらされます(たとえば、タイプミスはバグにつながるのではなく、コンパイル時にキャッチされます)。また、文字列のすべてのインスタンスをリファクタリングするために私達を許可するという文脈で使用されるように、その文字列の他の用途に影響することなく、(ドメインオブジェクトでかなり頻繁に起こるかもしれません。)
anotherdave

回答:


12

私の知る限り、問題は、String定数をenumにラップするだけでは言語ユーザーのニーズを十分に満たせないと考えられることです。これは公式で対処されています、JDK 7(プロジェクトコイン)メーリングリストで発表され機能提案で

私が提案を読んだことによると、enumsを使用する代替案は、タイプが肥大化するという理由で却下されました。便宜上、提案の関連部分を以下に引用し、列挙型に対応するステートメントはます太字でます

主な利点:提案が好ましい変化をもたらすのは何ですか?

一連の定数文字列値に基づいて選択された操作には、より規則的なコーディングパターンを使用できます。新しいコンストラクトの意味は、Java開発者には明らかなはずです。

主な利点:提案が採用された場合、プラットフォームはなぜ優れているのですか?

文字列ベースのディスパッチコードの潜在的に優れたパフォーマンス。

主な欠点:常にコストがかかります。

コンパイラの実装とテストの複雑さが多少増加しました。

代替案:言語を変更せずに、何らかの方法で利益と利点を得ることができますか?

いいえ。文字列の等価性の連鎖if-then-elseテストは潜在的に高価であり、目的の文字列値ごとに1つの切り替え可能な定数の列挙を導入すると、正当な理由なくプログラムに別の型が追加されます...


大胆な部分は非常に驚くべきことです
サイモンベルゴ

1
個人的には、@ Simonは間接的な扱いとして、「そのような考慮事項に対処するJava5以前の方法に固執し続けると、C#との競争を失います」と読みます。:)「Java5以前の方法」の例については、JDK-1223179を参照してください。文字列をオンにします -1995年に送信され、「修正しない」としてすぐに閉じます:「息を止めないでください。 」
gnat

ありがとう、提案で提案された理由を見るのは非常に興味深い。「正当な理由なしに」プログラムに型を導入していると言うのはまだ間違っていると感じます-私たちはオブジェクト指向プログラマーです!:)
anotherdave

5
@anotherdave「オブジェクト指向」は、これらが意味のある目的を果たす場合にのみ型を導入することを正当化します。どうやら、JCPの一般的な意見では、スイッチで文字列定数のブレインラッパーとして列挙型を使用しても意味のある資格がないと判明しました
-gnat

1
うわー、公式案が積極的にタイプの肥大化に対抗しているのを見て、(良い意味で)驚いています。爽やかです。型の膨張は、Javaの大きな問題です。
ベン・リー

7

コードを読みやすくするだけでなく、if/else if比較と比較してパフォーマンスが向上する可能性があります。変更が価値があるかどうかは、比較の回数によって異なります。文字列スイッチは、2つの個別のスイッチ命令として発行されます。最初はハッシュコードで動作するためlookupswitch、最終的にはO(log n)複雑になる傾向があります。2番目は常に完全O(1) tableswitchであるので、複合複雑さはまだO(log n)です。単一の直線的な一連のif/else ifステートメントは、ややO(n)複雑になります。

たとえば3つ以上の文字列を比較する場合、a switchはおそらくより読みやすくコンパクトです。ホットコードパスで多数の比較を行わない限り、違いに気付くことはほとんどありませんが、パフォーマンスは向上する可能性があります。


1
列挙型のスイッチではなく、文字列のスイッチを使用する理由をもっと質問していました。大きなif/elseブロックの悪い習慣を考えますが、現時点では、それを使用する理由はあまりありません。
-anotherdave

ただし、コマンドパターンと組み合わせたマップは、余分なタイプのコマンドがあるため、同じ複雑さで読みやすくなります
SpaceTrucker

3

文字列のスイッチは、enum値が外部から取得される場合、つまりデータベースに保存される場合に使用できます。

JDK 7のもう1つの注目すべき点はswitchif-elseコンストラクトよりもパフォーマンスが高いことです。

また、高速な文字列switchesの優れたユースケースは、ノードタイプと属性タイプで多くのスイッチを作成する必要がある場合のJSON / XMLストリーム解析です。私はこれのためのより良いオプションを考えることはできません。


1
「文字列の切り替えは、列挙値が外部から来た場合、つまりデータベースに保存された場合、かけがえのないものです。」:これについて詳しく説明していただけますか?switchステートメントの文字列はハードコーディングされています。データベースの文字列が変更されるとすぐに、コードは古くなっています。
ジョルジオ

さて、あなたは正しいenumです-Javaの静的な性質のため、この場合にsを使用できます。私がこれを書いていたとき、私Roleは通常、クラスとして提供され、に入れることができないセキュリティライブラリから考えていましたenum。別のケースは、動的にコンパイルされたJava / Groovyコードです-この状況では、まれですが、文字列スイッチがより良いオプションになる可能性があります。
アンドレイチャシェフ

2

シンプルさ

スイッチのサポートの文字列は、列挙型またはif-elseロジックに変換せずにデータを処理するのに役立ちます。Stringを有効にする方が簡単な場合があります。

JDK 7(プロジェクトコイン)メーリングリストでの機能の提案@gnat答え

対象の文字列値ごとに1つの切り替え可能な定数の列挙型を導入すると、正当な理由なくプログラムに別の型が追加されます...

If-Elseバージョン

これは短いですが、多くif'sは読みにくいです。そして、これは遅いです。

if (color.equals("red")) {
    System.out.println("Color is Red");
} else if (color.equals("green")) {
    System.out.println("Color is Green");
} else {
    System.out.println("Color not found");
}

列挙バージョン

列挙型を定義する必要があります。これは適切ですが、いつかは不要です。

enum Color {RED, GREEN}

通常通りの処理

try {
    switch (Color.valueOf(color)) {
        case RED:
            System.out.println("Color is Red");
            break;
        case GREEN:
            System.out.println("Color is Green");
            break;
    }
} catch (IllegalArgumentException e) {
    System.out.println("Color not found");
}

JDK 7-switchステートメントバージョンの文字列

追加の型を変換および定義せずに処理できます。

switch (color) {
    case "red":
        System.out.println("Color is Red");
        break;
    case "green":
        System.out.println("Color is Green");
        break;
    default:
        System.out.println("Color not found");
}

7
しかし、これは問題に対処していません:ここで列挙型の代わりに文字列を使用するのはなぜですか?
デイブニュートン

0

どの言語でもほとんどの改善はコードを読みやすくすることです。私はいつでも空想よりも読みやすいコードを好みます。

あなたはこれを信じないかもしれませんが、試してください:

public enum JettStaff {
  ADRIAN("Adrian German") {
    public String toString() {
      return name + " (dgerman@indiana.edu)";
    }
  },
  ARJIT("Arjit Sengupta") {
    public String toString() {
      return name + " (asengupt@indiana.edu)";
    }
  },

  // and on for the rest...

  private String name;

  public JettStaff(String n) { this.name = n; }
}

JettStaff x = JettStaff.SUZANNE;
System.out.println(x);

あなたのコード例と上記のコメントについてはわかりません。読みにくいものの例として列挙を意味しますか?
-anotherdave


2
これは非常に不自然です。列挙型に電子メールフィールドがない、またはこれがクラスではないふりをしている。
デイブニュートン

4
@ user2860598要点を説明する場合は、関連する例を使用してください。また、文字列定数のファットフィンガリングにも対応していません。
デイブニュートン

1
@ user2860598あなたがリンクした答えは、既に導入されていて、以前はEnumで「近似」できると言っていました。私のポイントは、たとえ列挙を使用したとしても、Enumの使用は依然として望ましいと思われることでした。
-anotherdave

0

列挙型は優れているため、使用できる場合は文字列の代わりにこれらを使用する必要があります。しかし、できなかった場合があります。たとえば、Javaの外部からの外部オブジェクトを操作する必要がある場合などです。何かを解析する必要があると想像してください。サーバーの応答、構成、ログファイルなどに似ています。探しているオプションがたくさんあり、それらを列挙する方法はありません。そのため、Java <7では、たくさんのスタックに陥ります。

  if (something.equals("foo")) {
    // foo processing
  } else 
  if (something.equals("bar")) {
   // bar processing
  }

名前でそれらを取得しようとするか、カスタムString-> Enumルーチンを提供することで、この場合でも列挙を使用できますが、時には不可能または実用的ではありません(つまり、あまりにも多くの列挙を作成する必要がある場合)

TLDR:純粋にJavaコードで作業しているときは必ずEnumsを使用する必要がありますが、外部オブジェクトでは常に使用できるとは限りません。私の説明が理にかなっていることを願っています。


あなたの外のデータとしている取引であれば、あなたはすでにあなたの中で必要なものを知るには十分知っているif文を、あなたはそれをラップする必要があり、とにかくなど、あなたはまだ列挙型を使用することができますを意味し、またはコマンドパターン
デイブ・ニュートン

@DaveNewtonはい、まだ列挙型を使用できますが、私の答えで述べたように、解析中にコードで一度だけ使用される列挙型の塊を作成する場合のように、時には実用的ではありません。

@dimoniyしかし、もし100のEnum値を作成しなければならなかった場合、switchの代替案では、100のcaseステートメントを持たなければならないということではないでしょうか?そんなに多くの変数がある場合、私は間違いなく列挙型の型安全性を好むでしょう。
-anotherdave

0

Stringsswitchステートメントで使用すると、コードがより読みやすく、読みやすく、プログラミングの悪い習慣だと思うという意見には同意しません。保存に使用する値を変更する場合、スイッチまたはif-elseifが発生するたびに値を変更する必要があります。コードのすべての部分にハードコードされた値を入れているため、これは良くありません。これらのハードコードされた値の1つを変更することにした場合、何をするつもりですか?すべてのコピーを検索して置き換えますか?

if-elseifステートメントでハードコーディングされた値がある場合、Java 1.5より前では、型の安全性をもたらすという理由だけで、定数プリミティブ値を使用する方がはるかに優れています。

OK、文字列値(HTTPリクエスト、ファイルなどから)を取得する場合があり、それらをプリミティブ値に変換するのは大変なことです。しかしEnum、この時点でsは良い仕事をしています。Enumに保存する値を変更する場合は、宣言するときに変更するだけです。コードの他の部分を変更する必要はありません。(もちろん、他の場所に保存されている値を変更する必要があります)。

汚くて怠codeなコードを実装するだけなら、それは完璧です。たくさんのEnums をコーディングしたくないのですが、大規模で複雑なソフトウェアはあなたを殺します。


-1

どんな情報が入ってくるのか、そしてそれが5つの州のみになりうることを知っているなら、 Enum。ただし、可能な状態が9000を超える可能性があり、42だけを検索する必要がある場合、すべての状態を入力する必要はないため、スイッチを使用することをお勧めします。

ほとんどの状況では、可能な状態が不明であるか、多くがあり、少数しか気にしない場合を除き、ほとんどの場合、Enumが選択される傾向があります。

しかし、なぜ彼らは今それを導入しましたか?よりクリーンなコードを可能にしたのは単なる変更でした。

1.5では、Enumのカスタムボディも作成できました。


しかし、他の8958の可能な値を1つのEnum状態に割り当てるだけではありませんか?
-anotherdave

@anotherdaveこれは、defaultin switchがプレイする場所です。デフォルト状態をEnum作成しない限り、デフォルト状態を使用できることはわかりませんが、このコードは肥大化しますが、a switchははるかにきれいです。

3
UNKNOWNEnumの状態が肥大化していると言うのは奇妙に思えますがdefault、スイッチの場合はそうではありません。
-anotherdave
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.