私は、Javaのenumが、プライベートコンストラクタと多数のパブリック静的メンバーを持つクラスにコンパイルされていることを知っています。与えられた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)
{
...
}
...
}
どの演算子を使うべきですか?
どちらも技術的に正しいです。 .equals()
のソースコードを見ると、それは単に==
に委ねられています。
私は==
を使います。
==
はenum
で使用できますか?はい:enumはインスタンスを比較するために==
を使うことを可能にする厳密なインスタンスコントロールを持ちます。これは言語仕様によって提供される保証です(私が強調します):
JLS 8.9列挙型
列挙型には、その列挙定数によって定義されたもの以外のインスタンスはありません。
列挙型を明示的にインスタンス化しようとするのはコンパイル時エラーです。
Enum
内のfinal clone
メソッドはenum
定数が複製されないことを保証し、シリアライゼーションメカニズムによる特別な処理は重複したインスタンスがデシリアライゼーションの結果として決して作成されないことを保証します。列挙型の反射的インスタンス化は禁止されています。まとめると、これら4つのことは、enum
型のインスタンスが、enum
定数で定義されたものを超えて存在しないことを保証します。各
enum
定数のインスタンスは1つだけなので、少なくとも1つがequals
定数を参照していることがわかっている場合は、2つのオブジェクト参照を比較するときにenum
メソッドの代わりに==
演算子を使用できます。 (equals
のEnum
メソッドは、その引数に対してsuper.equals
を呼び出すだけで結果を返すfinal
メソッドであるため、同一性比較を実行します。)
この保証はJosh Blochが推奨するほど強力です。シングルトンパターンを使用することを主張する場合、それを実装する最良の方法は単一要素enum
を使用することです(参照: Effective Java 2nd Edition、Item 3:シングルトンの強制)プライベートコンストラクタまたは列挙型を持つプロパティ ; Singleton のスレッドセーフ
==
と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==b
の場合に限りa.equals(b)
。クラスがこの保証を行う場合、そのクライアントはequals(Object)
メソッドの代わりに==
演算子を使用できます。これにより、パフォーマンスが向上する可能性があります。列挙型はこの保証を提供します。
まとめると、enum
で==
を使用するための引数は次のとおりです。
2つのenum値を比較するために==
を使用すると、各enum定数に対して1つのオブジェクトしかないため、うまくいきます。
ちなみに、equals()
を次のように書くのであれば、実際には==
を使ってnullセーフなコードを書く必要はありません。
public useEnums(SomeEnum a)
{
if(SomeEnum.SOME_ENUM_VALUE.equals(a))
{
...
}
...
}
これは、 左から定数を比較 として知られているベストプラクティスです。
他の人が言っているように、==
と.equals()
の両方ともほとんどの場合に動作します。完全に異なる種類のオブジェクトを比較していないというコンパイル時の確実性は有効であり有益ですが、2つの異なるコンパイル時型のオブジェクトを比較するという特定の種類のバグもFindBugsによって発見されます。 Eclipse/IntelliJはコンパイル時の検査を行うので、それを見つけたJavaコンパイラはそれほど安全性を高めることはありません。
しかしながら:
==
がNPEを投げないことは、==
の 不利益 です。 enum
を使って表現する必要がある追加の状態は、追加のインスタンスとしてnull
に追加できるため、null
型をenum
にする必要はほとんどありません。それが予想外にnull
であれば、私は==
よりも暗黙のうちにfalseと評価するよりもNPEを持っているほうがいいです。したがって、私は実行時により安全であるの意見に同意しません。 enum
の値を@Nullable
にしないようにする習慣を身に付けることをお勧めします。==
が速いであるという議論もまた偽物です。ほとんどの場合、コンパイル時の型がenumクラスである変数に対して.equals()
を呼び出すことになります。その場合、(enum
のequals()
メソッドはオーバーライドできないので)これは==
と同じであることがわかります。関数呼び出しコンパイラが現在これを行っているかどうかはわかりませんが、そうではなく、Java全体のパフォーマンス上の問題であることが判明した場合は、10万人のJavaプログラマが自分のプログラミングスタイルを変更するよりもコンパイラを修正します。特定のコンパイラバージョンのパフォーマンス特性.enums
はオブジェクトです。他のすべてのオブジェクト型の標準比較は==
ではなく.equals()
です。特にenums
をenum以外のクラスにリファクタリングした場合、誤ってObjectsをequals()
ではなく==
と比較してしまう可能性があるため、enum
を例外とするのは危険です。そのようなリファクタリングの場合、上からのIt worksポイントは間違っています。 ==
の使用が正しいことをあなた自身に納得させるためには、問題の値がenum
またはプリミティブのどちらであるかをチェックする必要があります。もしそれがenum
以外のクラスであった場合、それは間違っていますが、コードはまだコンパイルされるので見逃しやすいです。 .equals()
の使い方が間違っている唯一のケースは、問題の値がプリミティブな場合です。その場合、コードはコンパイルされないので、見逃しがちです。したがって、.equals()
は正しいと識別するのがはるかに簡単で、将来のリファクタリングに対してより安全です。実際には、Java言語は左側の値で.equals()を呼び出すためにObjectsに==を定義し、オブジェクト識別のために別の演算子を導入すべきだと思いますが、それはJavaが定義された方法ではありません。
まとめると、引数はenum
型に.equals()
を使用することを支持しています。
これを比較するためのおおまかなタイミングテストは次のとおりです。
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でこのテストを数回実行しました。 "=="はこれまでより少し速いです。
equals
の代わりに==
を使うのが好きです。
他の理由は、ここで既に説明した他の理由に加えて、それを認識せずにバグを導入する可能性があることです。この列挙型がまったく同じだが別々のパッケージに入っているとします(一般的ではありませんが、発生する可能性があります)。
最初のenum :
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
でnextのようなequalsを使用し、first.pckg.Category
であるが、それに気付かずに最初のenum(second.pckg.Category
)をインポートするとします。
import second.pckg.Category;
...
Category.JAZZ.equals(item.getCategory())
item.getCategory()
はfalse
なので、あなたはtrueを期待していますが、常にJAZZ
dueは別のenumです。そして見るのは少し難しいかもしれません。
したがって、代わりに演算子==
を使用すると、コンパイルエラーが発生します。
operator ==を "second.pckg.Category"、 "first.pckg.Category"に適用することはできません
import second.pckg.Category;
...
Category.JAZZ == item.getCategory()
列挙型の場合は両方とも正しいです。
Enum定数を比較するために==
以外のものを使用することは意味がありません。まるで class
オブジェクトとequals
を比較する - しないでください!
しかし、Sun JDK 6u10以前には厄介なバグ(BugId 6277781 )があり、歴史的な理由で興味深い場合があります。このバグは、逆シリアル化されたenumで==
を適切に使用することを妨げましたが、これは間違いなく多少角っぽいケースです。
Enumはpublic static final field
(immutable)で宣言された列挙型定数ごとに1つのインスタンス(シングルトンのような)を返すクラスなので、==
演算子を使用してequals()
メソッドを使わずに等価性をチェックできます
Enumが==で簡単に機能するのは、定義された各インスタンスもシングルトンであるためです。そのため、==を使った恒等比較は常にうまくいきます。
しかし、==を使用するのは列挙型で機能するため、すべてのコードがその列挙型の使用と密接に関連していることを意味します。
例えば、Enumはインターフェースを実装することができます。現在、Interface1を実装しているenumを使用しているとします。後で、誰かがそれを変更するか、同じインターフェースの実装として新しいクラスImpl1を導入します。その後、Impl1のインスタンスを使い始めると、==が以前に使用されていたために変更してテストするためのコードがたくさんあります。
したがって、正当な利益がない限り、グッドプラクティスと見なされるものに従うことが最善です。
別のオプションは Objects.equals
ユーティリティメソッドです。
Objects.equals( thisEnum , thatEnum )
Objects.equals
.equals()の代わりに等しい演算子==
どの演算子を使うべきですか?
3番目のオプションは、 equals
ユーティリティクラス Java 7に追加 以降にある静的な Objects
メソッドです。
これは Month
enumを使った例です。
boolean areEqual = Objects.equals( Month.FEBRUARY , Month.JUNE ) ; // Returns `false`.
この方法にはいくつかの利点があります。
true
false
NullPointerException
を投げる危険はありませんObjects.equals
で使用されているロジックは何ですか?
Java 10ソースコード of OpenJDK から、自分自身で確かめてください。
return (a == b) || (a != null && a.equals(b));
つまり、どちらにも長所と短所があります。
一方では、他の答えで説明されているように、==
を使用することに利点があります。
一方、何らかの理由でenumを別のアプローチ(通常のクラスインスタンス)に置き換える場合は、==
を使用すると問題が発生します。 (BTDT)
私はpolygenelubricants答えを補足したいです:
私は個人的にequals()を好みます。しかし、それは型の互換性チェックを妨げます。これは重要な制限だと思います。
コンパイル時に型の互換性をチェックするには、列挙型でカスタム関数を宣言して使用します。
public boolean isEquals(enumVariable) // compare constant from left
public static boolean areEqual(enumVariable, enumVariable2) // compare two variable
これにより、NPE保護、コードの読みやすさ、およびコンパイル時の型の互換性チェックという両方のソリューションのすべての利点が得られます。
列挙型に未定義の値を追加することもお勧めします。