Java列挙型と、静的な最終的なパブリックフィールドを持つクラスの利点は何ですか?


147

私はC#に非常に精通していますが、Javaでの作業が増えています。Javaの列挙型は基本的にC#の列挙型と同等であることがわかったはずですが、明らかにそうではありません。最初、私はJava enumに複数のデータが含まれている可能性があることを知って興奮しました(http://docs.oracle.com/javase/tutorial/java/javaOO/enum.html)。しかし、それ以来、列挙型要素に特定の値を簡単に割り当てる機能や、その結果、適切な労力をかけずに整数を列挙型に変換する機能など、C#でささいな多くの機能が見つからないことがわかりました(つまり、整数値を対応するJava Enumに変換します)。

だから私の質問はこれです:public static finalフィールドがたくさんあるクラスよりもJava enumに利点がありますか?それとも、よりコンパクトな構文を提供するだけですか?

編集:より明確にさせてください。同じ型の一連のpublic static finalフィールド持つクラスに対するJava enumの利点は何ですか?たとえば、最初のリンクの惑星の例では、これらのパブリック定数を持つクラスに対する列挙型の利点は何ですか?

public static final Planet MERCURY = new Planet(3.303e+23, 2.4397e6);
public static final Planet VENUS = new Planet(4.869e+24, 6.0518e6);
public static final Planet EARTH = new Planet(5.976e+24, 6.37814e6);
public static final Planet MARS = new Planet(6.421e+23, 3.3972e6);
public static final Planet JUPITER = new Planet(1.9e+27, 7.1492e7);
public static final Planet SATURN = new Planet(5.688e+26, 6.0268e7);
public static final Planet URANUS = new Planet(8.686e+25, 2.5559e7);
public static final Planet NEPTUNE = new Planet(1.024e+26, 2.4746e7);

私の知る限り、カサブランカの答えはこれを満足する唯一のものです。


4
@ボヘミアン:OPはpublic static finalフィールドにのみ言及しているため、重複していない可能性があります。フィールドは型付きの値であり、必ずしもintsである必要はありません。
カサブランカ2012

1
@Shahzebほとんどありません。文字列定数の代わりに列挙型を使用することは明らかに非常に良いアイデアであり、推奨される以上のものです。タイプセーフ、静的ユーティリティ関数などは必要ありません。代わりに文字列を使用する理由はまったくありません。
Voo

1
@Vooはい私は意見の相違があることを知っていました。そして、ここに49秒に1つあります。列挙型は素晴らしいです(そして、私はそれらを愛し、それらを非常に頻繁に使用します)が、定数を宣言するか、またはそれを使用するときに、どのようなタイプセーフティが必要ですか。String literalの定数を宣言する必要があるたびに列挙型を作成するのはやり過ぎです。
Shahzeb

4
@Shahzeb単一の変数がある場合は、必ず文字列を使用してください。ここではあまり起こりません(単一の値はパラメーターとしては無意味です)。しかし、私たちは定数について話しているSので、今はおそらくそれらを関数に渡すなどについて話していることに注意してください。型安全が必要ですか?まあ、違いますが、ほとんどの人は「すべてがvoid*良い」というcスタイルのタイプを検討し、バグを停止できます(特に、複数のenum / stringパラメータを渡す場合!)。また、定数を独自の名前空間などに配置します。これとは逆に、単純な変数を使用するだけの利点はありません。
Voo

3
@ボヘミアン:どうだかわかりません。int1は任意の値を渡す可能性があるための、何の型の安全性はありません。一方、型付きオブジェクトは、型安全性の点で列挙型と違いはありません。
カサブランカ

回答:


78

技術的には、列挙型を一連の型付き定数を持つクラスとして実際に見ることができます。これは実際、列挙型定数が内部的に実装されている方法です。enumただし、を使用すると、などの方法で自分で実装する必要がある便利なメソッド(Enum javadoc)が提供されますEnum.valueOf


14
.values()値のリストを反復処理することもできます。
h3xStream

1
これは正解のようですが、満足できるものではありません。私の意見では、よりコンパクトな構文とEnumメソッドへのアクセスのためだけにJavaが列挙型のサポートを追加することはほとんど価値がありませんでした。
クレイグW

7
@Craigあなたの本能は正しいです-これは列挙型の目的を完全に逃したので、これは非常に悪い答えです。理由の一部については、質問の下の私のコメントを参照してください。
ボヘミアン

1
@ボヘミアン:私は列挙型の「目的を逃した」のではなく、常に使用します。上記のコメントに対する私の返答を参照してください。
カサブランカ2012

1
Enum.valuesOfメソッドは、2回呼び出されても同じオブジェクトを返しますか?
EmreAktürk、2015年

104
  1. タイプセーフティとバリューセーフティ。
  2. シングルトンを保証します。
  3. メソッドを定義およびオーバーライドする機能。
  4. switchステートメントで値を使用する機能caseステートメントで。
  5. 値の組み込み逐次化 ordinal().
  6. 値ではなく名前でシリアル化し、ある程度の将来性を提供します。
  7. EnumSetEnumMapクラス。

19
とはいえ、コードを列挙型に入れるたびに、それを後悔しました。
ローン侯爵

4
どうして後悔したの?私はしたことはありません…
glglgl 2017年

2
@glglglこれは、アプリケーション固有のコードを、それが実際には属していないと感じた場所に配置したため、実際には値のセットを定義するだけでした。もう一度やらなければならなかったのなら、それをswitch使用する本来の動機であった多数のステートメントの1つに含めたでしょうEnum
ローン侯爵

72

それらをswitchステートメントで使用する能力については誰も言及していません。私もそれを投入します。

これにより、を使用したり、シーケンスをinstanceof混乱させifたり、文字列/ int以外の値を切り替えたりすることなく、任意に複雑な列挙をクリーンな方法で使用できます。正規の例は状態マシンです。


とにかく、列挙型フィールドと静的フィールドの利点については触れていません。静的フィールドのあるスイッチステートメントでは、十分な型を使用できます。Op実際の機能やパフォーマンスの違いが必要
Genaut 2017

@Genaut利点は、列挙型には文字列やintよりも多くの機能があることです。質問は、私が提供した違いについてでした。OPは、すでに列挙型が何であるかを認識している、と私はこの4.5歳前に投稿されたときに誰もがswitch文を言及しなかった、と少なくとも少数の人々はそれが新しい情報_(ツ)_ /¯提供見つかった
デイブ・ニュートン

44

主な利点はタイプセーフです。定数のセットを使用すると、同じ組み込み型の任意の値を使用でき、エラーが発生します。列挙型では、適切な値のみを使用できます。

例えば

public static final int SIZE_SMALL  = 1;
public static final int SIZE_MEDIUM = 2;
public static final int SIZE_LARGE  = 3;

public void setSize(int newSize) { ... }

obj.setSize(15); // Compiles but likely to fail later

public enum Size { SMALL, MEDIUM, LARGE };

public void setSize(Size s) { ... }

obj.setSize( ? ); // Can't even express the above example with an enum

3
クラスもタイプセーフです...:/(静的フィールドがコンテナクラスのタイプであると想定)
h3xStream

それでも無効な値を渡すことはできますが、発見するのがはるかに簡単なコンパイル時エラーになります。
zzzzzzzzzzzzzzzzzzzzzzzzzzzzzz

2
setSize(null)2番目の例を呼び出すこともできますが、最初の例のエラーよりもはるかに早く失敗する可能性があります。
ジェフリー

42

混乱が少なくなります。例を挙げましょうFont。必要な名前Font、サイズ、スタイル(new Font(String, int, int))を取るコンストラクターがあります。今日まで、スタイルとサイズのどちらが先かを思い出せません。場合はFont使用していたenumその異なるスタイルのすべてのために(PLAINBOLDITALICBOLD_ITALIC)、そのコンストラクタは次のようになりFont(String, Style, int)混乱を防ぎ、。残念ながら、クラスが作成されたenumときはは存在していませんでしたFont。Javaは逆の互換性を維持する必要があるため、このあいまいさに常に悩まされることになります。

もちろん、これは定数のenum代わりに使用するための単なる議論ですpublic static final。列挙型はシングルトンに最適で、デフォルトの動作を実装しながら、後でカスタマイズすることもできます(つまり、戦略パターン)。後者の例としては、java.nio.fileS」OpenOptionStandardOpenOption、開発者は自分の非標準を作成したい場合:はOpenOption、彼ができました。


同じ列挙型の2つが要求された場合、「フォント」のケースはまだあいまいです。その問題に対する標準的な答えは、多くの言語が名前付きパラメーターと呼ぶものです。それ以上のIDEメソッドシグネチャサポート。
aaaaaa 2015

1
@aaaaaa varargsまたはa をenum使用せずにコンストラクタが同じものを2つ使用して、それらを任意の数だけ取得するケースはあまり見かけません。Set
ジェフリー

@aaaaaa名前付きパラメーターの主な問題は、実装の詳細(パラメーターの名前)に依存しています。インターフェイスが作れますinterface Foo { public void bar(String baz); }。誰かが呼び出すクラスを作りますbarsomeFoo.bar(baz = "hello");。私はの署名変更するFoo::barにはpublic void bar(String foobar)。呼び出した人someFooは、コードを引き続き機能させたい場合、コードを変更する必要があります。
ジェフリー

列挙型が連続して表示されることも覚えていませんが、一般的なものはDAY_OF_WEEKか類似したものだと思いました。そして、インターフェースについての良い点-それを考えていませんでした。個人的には、名前のないパラメータによって引き起こされる広範囲にわたるあいまいさについてその問題を取り上げ、より強力なIDEサポートを必要とします。ただし、これは判断の呼びかけであり、APIの変更を壊すことは真剣に検討する必要があることです。
aaaaaa

26

ここには多くの良い答えがありますが、列挙型専用のコレクションAPIクラス/インターフェースの高度に最適化された実装があることについては言及されていません。

これらの列挙特定のクラスにのみ受け入れEnum(インスタンスEnumMapのみ受け入れますEnum sをキーとして)、可能な場合は常に、それらの実装でコンパクトな表現とビット操作に戻ります。

これは何を意味するのでしょうか?

私たちの場合はEnumタイプが何よりその64個の要素(現実のほとんどの持っていないEnum例が、このために対象となります)の実装は、単一の要素保存し、long値を、各Enum問題のインスタンスは、この64ビット長のビットに関連付けられますlong。要素をに追加するのEnumSetは、適切なビットを1に設定するだけです。削除は、そのビットを0に設定するだけです。要素がにあるかどうかのテストは、Set1つのビットマスクテストだけです。これであなたはEnumこれが大好きになります!


1
以前はこの2つについて知っていましたが、あなたのWhat does this mean?セクションから多くのことを学びました。サイズ64での実装に格差があることは知っていましたが、実際には理由がわかりませんでした
Christopher Rucinski '25

15

例:

public class CurrencyDenom {
   public static final int PENNY = 1;
 public static final int NICKLE = 5;
 public static final int DIME = 10;
public static final int QUARTER = 25;}

Java定数の制限

1)タイプセーフなし:まず、タイプセーフではありません。その値を表すコインはありませんが、99などの有効なint値を割り当てることができます。

2)意味のある印刷なし:これらの定数のいずれかの値を印刷すると、コインの意味のある名前ではなく数値が印刷されます。たとえば、NICKLEを印刷すると、「NICKLE」ではなく「5」が印刷されます

3)名前空間なし:currencyDenom定数にアクセスするには、PENNYを使用するのではなく、クラス名に接頭辞を付ける必要があります。たとえば、これはJDK 1.5の静的インポートを使用して実現することもできます。

列挙型の利点

1)Javaの列挙型はタイプセーフであり、独自の名前空間があります。これは、列挙型のタイプが以下の例の「通貨」のようになり、列挙型定数で指定された以外の値を割り当てることができないことを意味します。

public enum Currency {PENNY, NICKLE, DIME, QUARTER};

Currency coin = Currency.PENNY; coin = 1; //compilation error

2)JavaのEnumはクラスまたはインターフェースのような参照型であり、Java Enum型の次の例に示すように、CおよびC ++のEnumよりも強力なjava Enum内でコンストラクター、メソッド、および変数を定義できます。

3)以下の例に示すように、作成時に列挙定数の値を指定できます。public enum Currency {PENNY(1)、NICKLE(5)、DIME(10)、QUARTER(25)}; ただし、これが機能するには、PENNY(1)が実際にint値を受け入れるコンストラクターを呼び出しているため、メンバー変数とコンストラクターを定義する必要があります。以下の例を参照してください。

public enum Currency {
    PENNY(1), NICKLE(5), DIME(10), QUARTER(25);
    private int value;

    private Currency(int value) {
            this.value = value;
    }
}; 

リファレンス:https : //javarevisited.blogspot.com/2011/08/enum-in-java-example-tutorial.html


11

すでにお気づきのように、列挙型の最初の利点は構文の単純さです。しかし、列挙型の主なポイントは、デフォルトで範囲を形成し、型と値の安全性チェックを通じてより包括的なコード分析を実行するのに役立つ、よく知られた定数のセットを提供することです。

列挙型のこれらの属性は、プログラマーとコンパイラーの両方に役立ちます。たとえば、整数を受け入れる関数があるとします。その整数はどういう意味ですか?どのような値を渡すことができますか?あなたは本当にすぐにはわかりません。しかし、enumを受け入れる関数を見ると、渡せるすべての可能な値がよくわかります。

コンパイラーの場合、列挙型は値の範囲を決定するのに役立ちます。列挙型メンバーに特別な値を割り当てない限り、それらは0以上の範囲です。これは、タイプセーフティチェックなどを通じてコードのエラーを自動的に追跡するのに役立ちます。たとえば、コンパイラは、switchステートメントですべての可能な列挙値を処理していないことを警告する場合があります(つまり、defaultケース、N列挙値のうち1つだけを処理する場合)。enumの値の範囲は整数の値よりも小さいため、任意の整数をenumに変換すると警告が表示され、実際には整数を受け入れない関数でエラーが発生する可能性があります。また、スイッチのジャンプテーブルの生成は、値が0以上の場合に容易になります。

これは、Javaだけでなく、厳密な型チェックを行う他の言語にも当てはまります。C、C ++、D、C#が良い例です。


4

enumは暗黙的にfinalであり、プライベートコンストラクターでは、そのすべての値は同じタイプまたはサブタイプです。を使用してすべての値をvalues()取得するname()か、そのor ordinal()値を取得するか、数値または名前でenumをルックアップできます。

サブクラスを定義することもできます(概念的には最終的なものですが、他の方法では実行できません)。

enum Runner implements Runnable {
    HI {
       public void run() {
           System.out.println("Hello");
       }
    }, BYE {
       public void run() {
           System.out.println("Sayonara");
       }
       public String toString() {
           return "good-bye";
       }
    }
 }

 class MYRunner extends Runner // won't compile.

4

列挙型の利点:

  1. 列挙型はタイプセーフですが、静的フィールドはそうではありません
  2. 値の数には限りがあります(存在しない列挙値を渡すことはできません。静的クラスフィールドがある場合は、その間違いを犯す可能性があります)
  3. 各列挙型には、複数のプロパティ(フィールド/ゲッター)を割り当てることができます-カプセル化。また、いくつかの単純なメソッド:YEAR.toSeconds()など。比較:Colors.RED.getHex()とColors.toHex(Colors.RED)

「列挙型要素に特定の値を簡単に割り当てる機能など」

enum EnumX{
  VAL_1(1),
  VAL_200(200);
  public final int certainValue;
  private X(int certainValue){this.certainValue = certainValue;}
}

「その結果、適切な労力をかけずに整数をenumに変換する機能」 intをenumに変換するメソッドを追加します。マッピングjava enumを含む静的HashMap <Integer、EnumX>を追加するだけです。

本当にord = VAL_200.ordinal()をval_200に変換したい場合は、EnumX.values()[ord]を使用してください。


3

もう1つの重要な違いは、Javaコンパイラstatic finalプリミティブ型のフィールドと文字列をリテラルとして扱うことです。これは、これらの定数がインラインになることを意味します。これはC/C++ #defineプリプロセッサに似ています。このSOの質問を参照してください。これは列挙型には当てはまりません。



2

最大の利点は、列挙型シングルトンが記述しやすく、スレッドセーフであるということです。

public enum EasySingleton{
    INSTANCE;
}

そして

/**
* Singleton pattern example with Double checked Locking
*/
public class DoubleCheckedLockingSingleton{
     private volatile DoubleCheckedLockingSingleton INSTANCE;

     private DoubleCheckedLockingSingleton(){}

     public DoubleCheckedLockingSingleton getInstance(){
         if(INSTANCE == null){
            synchronized(DoubleCheckedLockingSingleton.class){
                //double checking Singleton instance
                if(INSTANCE == null){
                    INSTANCE = new DoubleCheckedLockingSingleton();
                }
            }
         }
         return INSTANCE;
     }
}

どちらも類似しており、実装することでシリアル化を単独で処理しました

//readResolve to prevent another instance of Singleton
    private Object readResolve(){
        return INSTANCE;
    }

もっと


0

内部ではコンパイラが各エントリのサブクラスを生成するため、私はenumできないと思います。finalenum

詳細情報ソースから


内部的には、それらは最終的なものではありません。なぜなら、あなたが言うように、内部的にサブクラス化できるからです。しかし、悲しいかな、自分自身でそれらをサブクラス化することはできません。たとえば、独自の値で拡張するためです。
glglgl 2017年

0

ここに掲載されている列挙型には多くの利点があり、質問で尋ねられたように、私は今そのような列挙型を作成しています。しかし、私は5〜6個のフィールドを持つ列挙型を持っています。

enum Planet{
EARTH(1000000, 312312321,31232131, "some text", "", 12),
....
other planets
....

このような場合、列挙型に複数のフィールドがある場合、コンストラクタと目玉を見る必要があるため、どの値がどのフィールドに属しているかを理解するのは非常に困難です。

static final定数を持つクラスとBuilderパターンを使用してそのようなオブジェクトを作成すると、読みやすくなります。ただし、必要に応じて、列挙型を使用する他のすべての利点を失うことになります。このようなクラスの欠点の1つは、Planetオブジェクトを手動でに追加する必要があることlist/setです。Planets.

私はまだ、そのようなクラスの上に列挙型を好むvalues()便利になると、あなたがそれらの中で使用する必要がある場合、あなたは知っていることはないswitchか、EnumSetまたはEnumMap将来的に:)


0

主な理由:列挙型は、パラメーターの意味論的意味が明確で、コンパイル時に強く型付けされている、適切に構造化されたコードを作成するのに役立ちます-他のすべての理由から。

代償: Javaの初期状態では、Enumのメンバーの配列が最後です。これは通常、値の安全性とテストに役立ちますが、たとえばライブラリから既存の基本コードを拡張する場合など、状況によっては欠点になることがあります。対照的に、同じデータが静的フィールドを持つクラスにある場合は、実行時にそのクラスの新しいインスタンスを簡単に追加できます(そのクラスのすべてのIterableにこれらを追加するコードを記述する必要がある場合もあります)。しかし、このEnumの動作は変更できます。リフレクションを使用すると、実行時に新しいメンバーを追加するか、既存のメンバーを置き換えることができますが、これはおそらく、代替手段がない特殊な状況でのみ行う必要があります。私の答えを見てください缶私はJavaで実行時に列挙の要素を追加および削除

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