Java列挙型メンバーの比較:==またはequals()?


1736

Java enumは、プライベートコンストラクターとパブリックスタティックメンバーの束を持つクラスにコンパイルされることを知っています。与えられた列挙型の2つのメンバーを比較するとき、私は常に使用しました.equals()、例えば

public useEnums(SomeEnum a)
{
    if(a.equals(SomeEnum.SOME_ENUM_VALUE))
    {
        ...
    }
    ...
}

しかし、私==は.equals()の代わりに等号演算子を使用するいくつかのコードに出くわしました:

public useEnums2(SomeEnum a)
{
    if(a == SomeEnum.SOME_ENUM_VALUE)
    {
        ...
    }
    ...
}

どの演算子を使用する必要がありますか?


7
:私は非常によく似た質問を偶然見つけstackoverflow.com/questions/533922/...
マット・ボール

39
すべての回答(特に==が機能する理由を詳細に説明するpolygenelubricantsからの回答)で、==のもう1つの大きな利点が言及されていないことに驚いています。オブジェクト)。equalsを使用すると、同じ列挙型の「代替」のインスタンスが何とかして浮かんでいる可能性があると考えるようになります。
Stuart Rossiter、2015年

6
複雑にしないでおく。「==」はより読みやすく、列挙型はデフォルトでシングルトンであるため機能します。参照
ロケシュ2018

回答:


1578

どちらも技術的に正しいです。のソースコードを見ると、.equals()単に==

==ただし、nullセーフになるため、を使用します。


64
個人的には、==を使用する理由として言及されている「null安全」は好きではありません。
Nivas、2009年

258
@Nivas:どうして?引数の順序を心配したいですか(Pascalの答えのように、左から定数を比較する場合にのみ適用されます)?値を使用.equals()する前に、値がnullでないことを常に確認しますか?私はそうではありません。
マットボール

86
コードを読み取るとき、「==」は読者が離れて、​​関係する型のソースを見て、それが列挙型であることがわかるまで、正しくないように見える場合があることに注意してください。その意味で、「。equals()」を読む方が邪魔になりません。
Kevin Bourrillion、2009年

60
@Kevin-ソースを表示するときにタイプを直接確認できるIDEを使用していない場合は、適切なIDEの問題ではなく、ごまかしています。
MetroidFan2002、2009年

63
ときどきコードがリファクタリングされ、列挙がクラスに置き換えられる可能性があります。その時点で==が正しいことが保証されなくなり、equalsはシームレスに機能し続けます。このため、等しいが明らかに優れた選択です。nullの安全性が必要な場合(nullの使用を最小限に抑えるように作成されていないコードベースで作業しているため)、Apache ObjectUtilsまたはGuavaのObjects#equal(または独自にロールすることができます)があります。ドナルド・クヌースの本で誰かが私を正当に平手打ちするのを恐れて、「パフォーマンス」という言葉にも触れません。
laszlok 2013年

1113

==上で使用できますenumか?

はい:列挙型には、インスタンスの==比較に使用できる厳密なインスタンスコントロールがあります。これは言語仕様によって提供される保証です(私が強調):

JLS 8.9列挙型

列挙型には、その列挙定数で定義されたもの以外のインスタンスはありません。

enum型を明示的にインスタンス化しようとすると、コンパイル時エラーになります。のfinal cloneメソッドEnumは、enum定数が複製されないようにし、シリアル化メカニズムによる特別な処理により、逆シリアル化の結果として重複インスタンスが作成されないようにします。列挙型の反射的なインスタンス化は禁止されています。これらの4つの要素enumにより、enum定数で定義されたインスタンスを超えて型のインスタンスが存在しないことが保証されます。

enum定数のインスタンスは1つしかないため、2つのオブジェクト参照を比較するときに、少なくとも1つが定数を参照することがわかっている場合は、メソッドの代わりに演算子を使用できます==equalsenum。(のequalsメソッドEnumは、final単にsuper.equalsその引数を呼び出して結果を返すため、同一性比較を実行するメソッドです。)

この保証は十分に強力で、Josh Blochが推奨しています。シングルトンパターンの使用を主張する場合、それを実装する最善の方法は、単一要素を使用することですenum効果的なJava 2nd Edition、アイテム3:シングルトンプロパティをプライベートコンストラクタまたは列挙型、また、シングルトンで安全スレッド


違いは何ですか==とはequals

注意として、==は一般に、の実行可能な代替ではないことを言う必要がありますequals。ただし(の場合などenum)は、考慮すべき2つの重要な違いがあります。

== 投げない NullPointerException

enum Color { BLACK, WHITE };

Color nothing = null;
if (nothing == Color.BLACK);      // runs fine
if (nothing.equals(Color.BLACK)); // throws NullPointerException

== コンパイル時に型の互換性チェックが行われます

enum Color { BLACK, WHITE };
enum Chiral { LEFT, RIGHT };

if (Color.BLACK.equals(Chiral.LEFT)); // compiles fine
if (Color.BLACK == Chiral.LEFT);      // DOESN'T COMPILE!!! Incompatible types!

すべき ==該当するに使用するがありますか?

Blochは、インスタンスを適切に制御する不変クラスが、クライアントに==使用可能であることを保証できることを具体的に述べています。enum具体的に例示するために言及されています。

項目1:コンストラクターの代わりに静的ファクトリーメソッドを検討する

[...]不変クラスは、2つの等しいインスタンスが存在しないことを保証a.equals(b)できa==bます。クラスがこのことを保証する場合、そのクライアント==equals(Object)メソッドの代わりに演算子を使用でき、パフォーマンスが向上する可能性があります。列挙型はこの保証を提供します。

要約すると、==on を使用するための引数enumは次のとおりです。

  • できます。
  • より速いです。
  • 実行時に安全です。
  • コンパイル時に安全です。

27
いい答えですが... 1. 機能します:合意されました2. より高速です:列挙型に対して証明します:) 3. 実行時に安全です:Color nothing = null;バグはIMHOであり、修正する必要があります。列挙型には3つではなく2つの値があります( Tom Hawtinのコメントを参照してください)4. コンパイル時の方が安全です。OKですが、equals戻りfalseます。最後に、はそれを購入しませんequals()。読みやすさを優先します(Kevinのコメントを参照)。
Pascal Thivent、

201
これはJavaに対するブラックマークであり、一部の人々はそれfoo.equals(bar)をより読みやすいと考えていますfoo == bar
Bennett McElwee

19
それは機能します:リファクタリングの一部としてenumをクラスに置き換える必要があるまで。壊れる==を使用してすべてのコードを見つけてください。それはより高速です:真(疑わしい)であっても、時期尚早の最適化に関するKnuthの引用があります。実行時の安全性:==の使用がNullPointerExceptionからユーザーを分離する唯一の方法である場合、「安全」は思い浮かぶ最初の言葉ではありません。コンパイル時の安全性:それほどでもない。それは、間違った型を比較す​​るというかなり基本的な間違いからあなたを守ることができますが、繰り返しになりますが、それがプログラマーの愚かさであるならば、「安全」はあなたがそうでないものの1つです。
laszlok 2013年

35
「それがプログラマーの馬鹿げている場合、「安全」はあなたがそうでないものの1つです。」-私はこの見積もりに同意しません。これが型チェックのすべてです。つまり、コンパイル時、つまりコードが実行される前でも、「おかしな」間違いを防ぎます。賢い人でも、間違えやすい間違いを犯します。約束よりも保証がいくつかあります。
ymajoros

7
@laszlok:確かに、しかし物事が失敗する可能性がある場合、彼らはただ失敗するでしょう。いくつかの愚かな間違いのためのフェンスを持つことは常に良いことです。ささいなバグを修正するための創造的な余地はたくさんあります。コードを実行する前に、コンパイラーにこれを処理させてください。
ymajoros 2014

88

使用する ==2つのenum値を比較するためにすると、各enum定数に対して1つのオブジェクトしかないため、機能します。

余談ですが、実際に次のよう==に記述すれば、nullセーフコードを記述するためにを使用する必要はありませんequals()

public useEnums(final SomeEnum a) {
    if (SomeEnum.SOME_ENUM_VALUE.equals(a)) {
        
    }
    
}

これは、左から定数比較と呼ばれるベストプラクティスであり、必ず従う必要があります。


9
.equals()文字列値を入力するときにこれを行いますが、それでもnullセーフコードを書くのは簡単な方法だと思います。私はむしろ演算子(またはメソッド)が欲しいですが、[nullオブジェクト] .equalsは、ドット演算子がどのように機能するかにより、Javaではnullセーフにならないことを知っています。意味的には、「等しい」演算子は常に通勤する必要があります。
マットボール

2
まあ、JavaにはGroovy(?.)のSafe Navigation演算子がないので、定数と比較するときもこの手法を使い続けます。TBH、私はそれをくだらないとは思わず、おそらく「自然」ではないかもしれません。
Pascal Thivent 2009年

32
null列挙型は、一般的なエラーです。すべての可能な値のこの列挙を取得しました。ここにもう1つあります!
トム・ホーティン-2009年

8
として明示的に注釈が付けられている値を除き、左から定数を比較は@Nullable、どの値がnullになり得るか、またはnullになり得ないかについてのあいまいさを広げるため、アンチパターンと見なします。列挙型はほとんど決してであってはならないため、null値はプログラミングエラーになります。そのような場合、NPEをスローするのが早ければ早いほど、NPEを許可していたところでプログラミングの誤りを見つける可能性が高くなります。したがって、列挙型の場合、「ベストプラクティス...必ず従うべき」は間違いです。@Nullable
トビアス2014年

24
左から定数を比較すると、ヨーダスタイルのような最高の英語がベストプラクティスです。
maaartinus 14

58

他の人が言ったように、両方==.equals()ほとんどの場合で動作します。他の人が指摘した完全に異なるタイプのオブジェクトを比較していないというコンパイル時の確実性は有効かつ有益ですが、2つの異なるコンパイル時タイプのオブジェクトを比較する特定の種類のバグは、FindBugs(そしておそらくEclipse / IntelliJコンパイル時検査)、Javaコンパイラがそれを検出しても、それほど多くの安全性が追加されるわけではありません。

しかしながら:

  1. ==私の心の中でNPE を決して投げないという事実は、の欠点です==。を介して表現したい追加の状態を追加のインスタンスとしてに追加できるため、enum型をにする必要はほとんどありません。予想外の場合は、静かにfalseと評価するのではなく、NPE を使用します。したがって、実行時の意見では安全であることに同意しません。値を決してさせない習慣に入るのが良いですnullnullenumnull==enum@Nullableです。
  2. 引数==速くはまた、偽です。ほとんどの場合.equals()、コンパイル時の型が列挙型クラスである変数を呼び出します。これらの場合、コンパイラーはこれが==enumequals()メソッドをオーバーライドできないため)と同じであることを認識し、関数呼び出しを最適化できます。離れて。コンパイラが現在これを行っているかどうかはわかりませんが、そうでなく、全体的にJavaのパフォーマンスの問題であることが判明した場合は、10万人のJavaプログラマがプログラミングスタイルを適切に変更するのではなく、コンパイラを修正します。特定のコンパイラバージョンのパフォーマンス特性。
  3. enumsオブジェクトです。他のすべてのオブジェクトタイプでは、標準の比較はであり.equals()、ではありません==。特にを非列挙クラスにリファクタリングする場合は、enums誤ってObjectをでは==なくと比較してしまう可能性があるため、例外を設けることは危険だと思います。そのようなリファクタリングの場合、上からのIt works pointは間違っています。の使用が正しいことを納得させるには、問題の値がかプリミティブかを確認する必要があります。それが非でequals()enum==enumenumクラス間違いですが、コードはまだコンパイルされるため、見落としがちです。の使用の唯一のケース.equals()問題の値がプリミティブである場合は間違っています。その場合、コードはコンパイルされないため、見落とすことがはるかに難しくなります。したがって、.equals()正しいと識別するのははるかに簡単で、将来のリファクタリングに対して安全です。

私は実際、Java言語は左側の値で.equals()を呼び出すために==オブジェクトを定義し、オブジェクトIDに別の演算子を導入する必要があると思いますが、それはJavaの定義方法ではありません。

要約すると、私はまだ議論が型に使用すること.equals()を支持していると思いenumます。


4
さて、ポイント3についてenumは、switchターゲットとして使用できるので、少し特別です。Stringsも少し特別です。
CurtainDog 2014

switchステートメントの構文には==も.equals()も含まれていないので、あなたの目的はわかりません。私のポイント3.)は、読者がコードを検証するのがいかに簡単かについてです。switchステートメントが等しい限り、switchステートメントの等価セマンティクスは正しいです。
Tobias

17

==equals次の代わりに使用することを好みます:

ここで既に説明した他の理由に加えて、気づかないうちにバグを導入する可能性があるという理由もあります。まったく同じですが、個別のパッケージにあるこの列挙型があるとします(一般的ではありませんが、発生する可能性があります)。

最初の列挙

package first.pckg

public enum Category {
    JAZZ,
    ROCK,
    POP,
    POP_ROCK
}

2番目の列挙:

package second.pckg

public enum Category {
    JAZZ,
    ROCK,
    POP,
    POP_ROCK
}

そして、あなたは次のように等号を使用すると仮定item.categoryしているfirst.pckg.Categoryが、あなたは、第二の列挙型を(インポートsecond.pckg.Category、それを実現することなく最初の代わりに):

import second.pckg.Category;
...

Category.JAZZ.equals(item.getCategory())

あなたはオールウェイズを取得するので、falseあなたは本当の理由は、期待が異なる列挙型があるためitem.getCategory()ですJAZZ。そして、それを見るのは少し難しいかもしれません。

したがって、代わりに演算子==を使用すると、コンパイルエラーが発生します。

演算子==は、「second.pckg.Category」、「first.pckg.Category」には適用できません

import second.pckg.Category; 
...

Category.JAZZ == item.getCategory() 

あなたが言及した非常に良い点。したがって、私がすることは、myenum.value()をapplayし、NPEの安全性のために==比較を行うことです。
Javaメイン

14

2つを比較する大まかなタイミングテストを次に示します。

import java.util.Date;

public class EnumCompareSpeedTest {

    static enum TestEnum {ONE, TWO, THREE }

    public static void main(String [] args) {

        Date before = new Date();
        int c = 0;

        for(int y=0;y<5;++y) {
            for(int x=0;x<Integer.MAX_VALUE;++x) {
                if(TestEnum.ONE.equals(TestEnum.TWO)) {++c;}
                if(TestEnum.ONE == TestEnum.TWO){++c;}              
            }
        }

        System.out.println(new Date().getTime() - before.getTime());
    }   

}

IFを1つずつコメント化します。以下は、逆アセンブルされたバイトコードでの上記の2つの比較です。

 21  getstatic EnumCompareSpeedTest$TestEnum.ONE : EnumCompareSpeedTest.TestEnum [19]
 24  getstatic EnumCompareSpeedTest$TestEnum.TWO : EnumCompareSpeedTest.TestEnum [25]
 27  invokevirtual EnumCompareSpeedTest$TestEnum.equals(java.lang.Object) : boolean [28]
 30  ifeq 36

 36  getstatic EnumCompareSpeedTest$TestEnum.ONE : EnumCompareSpeedTest.TestEnum [19]
 39  getstatic EnumCompareSpeedTest$TestEnum.TWO : EnumCompareSpeedTest.TestEnum [25]
 42  if_acmpne 48

最初の(等しい)は仮想呼び出しを実行し、スタックからの戻りブール値をテストします。2番目(==)は、スタックから直接オブジェクトのアドレスを比較します。最初のケースでは、より多くの活動があります。

このテストは、一度に両方のIFで数回実行しました。「==」はこれまでよりもわずかに高速です。


コンパイラの分岐予測がこのテストを無効にするのではないかと思います。スマートコンパイラは、両方のifステートメントが常にfalseと評価されるため、複数の比較を行わないことを認識します。本当にスマートなコンパイラは、yループとxループの値が結果に影響を及ぼさず、全体を最適化することさえ認識するかもしれません...
Darrel Hoffman

それは可能ですが、間違いなくありません。実際のコンパイラで生成されたバイトコードを私の回答に示しています。
ChrisCantrell 2015

あなたはどちらも混乱しています。:-)分岐予測は実行時に機能するため、異なるバイトコードはあまり関係ありません。ただし、この場合、分岐予測は両方の場合に等しく役立ちます。
vidstige 2015

7
誰も気にしない?ビデオゲーム中にループ内で列挙を数千回比較している場合を除いて、追加のナノ秒は意味がありません。
user949300

バイトコードは、インタープリターモードを強制しない限り、パフォーマンスにはほとんど関係ありません。JMHベンチマークを作成して、パフォーマンスがまったく同じであることを確認します。
maaartinus 2017


7

tl; dr

別のオプションは、Objects.equalsユーティリティメソッドです。

Objects.equals( thisEnum , thatEnum )

Objects.equals ヌル安全のため

.equals()の代わりに演算子==に等しい

どの演算子を使用する必要がありますか?

3番目のオプションはequalsObjects Java 7以降に追加ユーティリティクラスです。

これは、 Month列挙型ます。

boolean areEqual = Objects.equals( Month.FEBRUARY , Month.JUNE ) ;  // Returns `false`.

利点

この方法にはいくつかの利点があります。

  • ヌルセーフティ
  • コンパクトで読みやすい

使い方

によって使用されるロジックは何ですか Objects.equalsですか?

自分自身を参照してくださいJavaの10のソースコードOpenJDKの

return (a == b) || (a != null && a.equals(b));

6

==列挙定数を比較する以外に何かを使用することはナンセンスです。それはオブジェクトを比較するclassequalsようなものです –しないでください!

ただし、Sun JDK 6u10以前には、歴史的な理由から興味深いかもしれない厄介なバグ(BugId 6277781)がありました。このバグにより==、デシリアライズされた列挙型を適切に使用できなくなりました。


4

列挙型は、public static final field(不変)によって宣言された列挙定数ごとに1つのインスタンス(シングルトンなど)を返すクラスであり、==演算子を使用してequals()メソッドを使用するのではなく、それらの等価性をチェックできます


1

enumが==で簡単に機能する理由は、定義された各インスタンスもシングルトンであるためです。したがって、==を使用したID比較は常に機能します。

ただし、==を使用すると、列挙型で機能するため、すべてのコードがその列挙型の使用と密接に結合されます。

例:Enumはインターフェースを実装できます。現在、Interface1を実装する列挙型を使用しているとします。後で、誰かがそれを変更するか、同じインターフェースの実装として新しいクラスImpl1を導入します。次に、Impl1のインスタンスの使用を開始すると、==の以前の使用のために、変更およびテストする多くのコードが必要になります。

したがって、正当な理由がない限り、良い方法と見なされる方法に従うことが最善です。


いい答えですが、すでにここで言及さています
shmosel 2017

ええと。その時点で問題になるのは、インターフェイスで==またはequalsを使用するかどうかです。さらに、不変型の実装を可変実装に置き換えることが本当に賢明かどうかについてのあらゆる種類の質問。そして、正当な利益を心配する前に、良い実践とは何かを理解することはおそらく賢明です。(imho ==の方が良い習慣です)。
Robin Davies

1

ソナールールの1つはEnum values should be compared with "=="です。その理由は次のとおりです。

equals()enumはObjectであり、すべてのJava開発者が==Objectのコンテンツの比較に使用すべきではないことを知っているため、enum値との等価性のテストは完全に有効です。同時に、==列挙型で使用:

  • 同じ期待される比較(コンテンツ)を提供します equals()

  • よりもnullセーフです equals()

  • 実行時チェックではなくコンパイル時(静的)チェックを提供します

これらの理由から、の使用を==に優先する必要がありますequals()

最後に==重要なことですが、on enumはと比べて間違いなく読みやすく(冗長ではありません)ですequals()


0

つまり、どちらにも長所と短所があります。

一方==で、他の回答で説明されているように、を使用することには利点があります。

一方、何らかの理由で列挙型を別のアプローチ(通常のクラスインスタンス)に置き換えた場合、使用したこと==が問題になります。(BTDT。)


-1

私はpolygenelubricantsの答えを補完したいと思います:

個人的にはequals()を好みます。ただし、型の互換性チェックは必要です。これは重要な制限だと思います。

コンパイル時に型の互換性チェックを行うには、列挙型でカスタム関数を宣言して使用します。

public boolean isEquals(enumVariable) // compare constant from left
public static boolean areEqual(enumVariable, enumVariable2) // compare two variable

これにより、NPE保護、コードの読みやすさ、コンパイル時の型互換性チェックなど、両方のソリューションの利点がすべて得られます。

列挙型にUNDEFINED値を追加することもお勧めします。


4
なぜこのソリューションはより優れているの==ですか?では==あなたが読みやすいコード、およびコンパイル時の型の互換性チェックNPE保護を取得し、あなたはそれらのすべてを取得する任意のカスタムに等しい-ような方法記述せず。
マットボール

1
UNDEFINED値は、おそらく良いアイデアです。もちろん、Java 8 Optionalでは代わりに使用します。
トーマス
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.