このお話は、みなさんよくご存じだと思います。
ふだん使う意味での「値の比較」では、equalsですね。
とくに、Javaをはじめて習うときには、とにかくequalsを使え!だと思いますw
でも、何が違うんでしょう?
まずは試してみる
とりあえず、Stringでお試ししてみましょう。
若干Stringはイヤらしい部分があるクラスですが、、、見やすいと思うんで、まぁこれで。
いくつか並べていますが、「値の比較」って意味で、期待しているのはどれもtrueですw
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | public class StringEqualsTest { public static void main(String[] args) { String abc1 = new String("ABC"); String abc2 = new String("ABC"); String c = "C"; System.out.println("☆☆☆ ==で比較 ☆☆☆"); System.out.println("1.." + ( "ABC" == "ABC") ); //リテラル同士 System.out.println("2.." + ( ("AB" + c) == "ABC") ); //リテラル・変数混じりvsリテラル System.out.println("3.." + ( abc1 == abc2) ); //newした変数同士 System.out.println("☆☆☆ equalsで比較 ☆☆☆"); System.out.println("1.." + ( "ABC".equals("ABC")) ); System.out.println("2.." + ( ("AB" + c).equals("ABC")) ); System.out.println("3.." + ( abc1.equals(abc2)) ); } } |
実行結果
実行結果はこんな感じ。
“==”と”equals”は、結果が違いますね。
1 2 3 4 5 6 7 8 | ☆☆☆ ==で比較 ☆☆☆ 1..true 2..false 3..false ☆☆☆ equalsで比較 ☆☆☆ 1..true 2..true 3..true |
いやー、うまくいきませんねーw
というわけで、”==”は期待値どおりにならないときがありますね。”equals”は、とうぜん期待通り。
※一番下に書いてますが、”==”の比較で1ばんがtrueな件は別の機会に。今回は無視してくださいw
「==」の比較とは
みなさんご存じだと思うので、答えだけ簡単に。
「==」は、モノが同一であることの比較です。よく「同一性」なんて言われるものです。
つまり、(参照先が)同じオブジェクトですね。
というわけで、値が同じでも違うオブジェクトであれば、falseってわけですね。
「equals」の比較とは
最初に書いてありますがw、「値の比較」です。
つまり、同じ値を持つなら、別のオブジェクトだろうがtrueです。
こちらは「同値性」なんて言われます。
というわけで、値を比較するにはこっち。ってわけですね。。
んで、同値性ってなに?
同一性は同じモノなので、それ以上に言うことはないのですが、
同値性の方は、少し補足。これで終わっても面白くないし、一言だけ言われても・・ねぇw
というわけで、java.lang.Objectクラスのequals(java.lang.Object)メソッドの解説を見ていきます。
equalsメソッドは、null以外のオブジェクト参照での同値関係を実装します。
- 反射性(reflexive): null以外の参照値xについて、x.equals(x)はtrueを返します。
- 対称性(symmetric): null以外の参照値xおよびyについて、y.equals(x)がtrueを返す場合に限り、x.equals(y)はtrueを返します。
- 推移性(transitive): null以外の参照値x、y、およびzについて、x.equals(y)がtrueを返し、y.equals(z)がtrueを返す場合、x.equals(z)はtrueを返します。
- 一貫性(consistent): null以外の参照値xおよびyについて、x.equals(y)の複数の呼出しは、このオブジェクトに対するequalsによる比較で使われた情報が変更されていなければ、一貫してtrueを返すか、一貫してfalseを返します。
- null以外の参照値xについて、x.equals(null)はfalseを返します。
長いんで・・・ざっくりw(null云々は当たり前なんで省いた)
- 反射性(reflexive): AとAを比較したらtrue。
- 対称性(symmetric): A=Bならば、B=A。
- 推移性(transitive): A=B,B=Cならば、A=C。
- 一貫性(consistent): 中身が変わらない限り、何回equalsを呼び出しても結果は変わらない。
というのが、equalsで実装されている(されるべき)こと。すなわち、これが同値性ってわけです。
ここまでが、==とequalsの違いのお話でした。
自作クラスでequalsしてみる
ここからは、少しおまけ的なとこです。
今までの話でequalsを使えば値の比較がOKってことなんで、equalsを使って、自作のクラスで比較してみます。
フィールドを1つ持ったクラスに値を詰めて比較してみます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | public class StringEqualsTest2 { private String a = null; public static void main(String[] args) { StringEqualsTest2 s1 = new StringEqualsTest2(); s1.a = "aaa"; StringEqualsTest2 s2 = new StringEqualsTest2(); s2.a = "aaa"; System.out.println(s1.equals(s2)); } } |
実行結果
実行結果はこんな感じ。
1 | false |
falseですねー。おかしいですねー。値は合ってますけどねー。
自作のクラスを比較するにはequalsのオーバーライドが必要
というわけで、こんな感じで、equalsメソッドをオーバーライドします。
自作のクラスもObjectクラスを(暗黙的に)継承しているんで、当然equalsメソッドを継承しています。
ただ、Objectクラスからすれば、値が同じとは??って感じで、継承下のクラスで何を比較すれば、値が一緒なのかはわかりません。
よって、equalsのオーバーライドが必要ということになります。
つまり、equalsをオーバーライドするということは、オブジェクト同士で値を比較するときの「比較すべき対象とその方法(≒同値性)」を教えている。ってことになりますね。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | public class StringEqualsTest2 { private String a = null; public static void main(String[] args) { StringEqualsTest2 s1 = new StringEqualsTest2(); s1.a = "aaa"; StringEqualsTest2 s2 = new StringEqualsTest2(); s2.a = "aaa"; System.out.println(s1.equals(s2)); } @Override public boolean equals(Object obj) { StringEqualsTest2 o = (StringEqualsTest2)obj; if(o == null) { return false; } else if(a == null && o.a == null) { return true; } else if(a == null && o.a != null) { return false; } return a.equals(o.a); } @Override public int hashCode() { return a.hashCode(); } } |
実行結果
実行結果はこんな感じ。
1 | true |
良いですね。
hashcodeメソッドのオーバーライドもしよう!
さっきのコードには、こっそり余計なものがついてますw
hashcodeのオーバーライドです。
これは本筋からさらに逸れるので、詳しくは別の機会にお話しします。
ざっくり言うと、equalsメソッドとhashcodeメソッドは深い関係にあります。
というわけで、equalsをオーバーライドするときは、一緒にhashcodeメソッドもオーバーライドをした方が良いです。(すべきです!)
—
今回はここまでにしておきます。させてください。
すみません。書いてみたら途中で思った以上に長いことに気づいてしまいました。
hashcodeメソッドの件と、Stringはイヤらしい部分があるクラスの件(1ばんがtrueな件)は、また別の機会に書きたいと思います。