Javaを使ったことのない私がJavaでプログラムを書こうとすると、罠にハマります。
Javaでは、等価演算子(==)による比較は、オブジェクトの値の一致ではなくインスタンスの一致を比較します。
(クラスを自作する際には equals メソッドを正しく実装しましょう。)
これを理解して十分に気をつけていれば、この罠にハマることはありません。
ところが、誤ってオブジェクトの値の比較を == で行ってしまった場合に、すぐには気づけないことがあります。
それが以下の例です。
もう少し発展した例を見てみましょう。
では、これはどうでしょう。
これは、プリミティブ整数型のラッパークラス(Integer や Byte など)と Character、Boolean のみで起こります。
Float や Double では、変更後の値が一致しても同一インスタンスとしては扱われません。
さて、既に十分面倒な罠を見てきたのですが、実はもう1つ恐ろしい罠があります。
この記事を書くきっかけになったのがこれです。
最初のほうで見た例とほとんど変わらないのに、== ではなく != です。
それ以外の違いといえば・・・そう、代入している値が違います。
もっと分かりやすく書いてみます。
値が [-128, 127] の範囲にあって明示的にインスタンスを生成していない(auto boxing を使用している)とき、インスタンスが一致します。
値が -128 未満または 127 より大きいときや、明示的にインスタンスを生成してから変更していないときにはインスタンスが一致しません。
Character の場合も、文字コードが 127 以下である通常の半角文字はインスタンスが一致しますが、文字コードが 128 以上になるものや全角文字はインスタンスが一致しません。
ややこしいです。
結論: クラス型の値を比較したいときは equals を使おう。
(おまけ)
実は、内容の一致する文字列リテラルも、同じ String のインスタンスとして扱われます(よくある最適化ですね)。
変更後は別のインスタンスになります。
参考:
Java 基礎知識( オブジェクトの比較 )【 Okapi Project 】
http://www.okapiproject.com/java/java_ref/foundation/manual_06.htm
javaのオブジェクト型(Integer)を==比較すると危険! | ジャイアントモリンキーのjavaテック
http://javatechnology.net/java/integer-equal/
Javaでは、等価演算子(==)による比較は、オブジェクトの値の一致ではなくインスタンスの一致を比較します。
Integer a = new Integer(46); Integer b = a; Integer c = new Integer(46); assert a == b; assert a != c;値を比較する際には、各クラスに実装されている equals メソッドを使います。
Integer a = new Integer(46); Integer b = a; Integer c = new Integer(46); assert a.equals(b); assert a.equals(c);ここまでは自分も知っていたので問題ありませんでした。
(クラスを自作する際には equals メソッドを正しく実装しましょう。)
これを理解して十分に気をつけていれば、この罠にハマることはありません。
ところが、誤ってオブジェクトの値の比較を == で行ってしまった場合に、すぐには気づけないことがあります。
それが以下の例です。
Integer a = 46; Integer b = 46; assert a == b;実は、同じ値の数値リテラルや文字列リテラルは、同一のインスタンスへの参照として扱われます。
もう少し発展した例を見てみましょう。
Integer a = 46; Integer b = 46; assert a == b; a += 1; assert a != b; b += 1; assert a == b;この例で分かる通り、途中で値を変更するような操作をしても、変更後の値が一致していればインスタンスも一致します。
では、これはどうでしょう。
Integer a = 46; Integer b = new Integer(46); assert a != b; a += 1; b += 1; assert a == b;なんと、new で個別にインスタンスを生成した場合であっても、その後変更を加えて値が等しくなれば同一インスタンスとして扱われます。
これは、プリミティブ整数型のラッパークラス(Integer や Byte など)と Character、Boolean のみで起こります。
Float や Double では、変更後の値が一致しても同一インスタンスとしては扱われません。
さて、既に十分面倒な罠を見てきたのですが、実はもう1つ恐ろしい罠があります。
この記事を書くきっかけになったのがこれです。
Integer a = 514; Integer b = 514; assert a != b;あれ?
最初のほうで見た例とほとんど変わらないのに、== ではなく != です。
それ以外の違いといえば・・・そう、代入している値が違います。
もっと分かりやすく書いてみます。
Integer a = 127, b = 127, c = 128, d = 128; assert a == b; assert c != d;実は、整数型のラッパークラスのインスタンスを == で比較した結果は、その値の大きさによっても変わります。
値が [-128, 127] の範囲にあって明示的にインスタンスを生成していない(auto boxing を使用している)とき、インスタンスが一致します。
値が -128 未満または 127 より大きいときや、明示的にインスタンスを生成してから変更していないときにはインスタンスが一致しません。
Character の場合も、文字コードが 127 以下である通常の半角文字はインスタンスが一致しますが、文字コードが 128 以上になるものや全角文字はインスタンスが一致しません。
ややこしいです。
結論: クラス型の値を比較したいときは equals を使おう。
(おまけ)
実は、内容の一致する文字列リテラルも、同じ String のインスタンスとして扱われます(よくある最適化ですね)。
変更後は別のインスタンスになります。
String a = "abc"; String b = "abc"; assert a == b; a += "def"; b += "def"; assert a != b;
参考:
Java 基礎知識( オブジェクトの比較 )【 Okapi Project 】
http://www.okapiproject.com/java/java_ref/foundation/manual_06.htm
javaのオブジェクト型(Integer)を==比較すると危険! | ジャイアントモリンキーのjavaテック
http://javatechnology.net/java/integer-equal/