Integer x = 1;
System.out.println(x == 1.0); // true
System.out.println(x.equals(1.0)); // false
==では二項数値昇格が起こるのにInteger.equalsの実装はそうなっていない、これはバグじゃないか、という話ですよね。
Integer#equals()の仕様は「実引数が null でなく,このIntegerオブジェクトと同じ int 値を表現するIntegerオブジェクトである場合に,またその場合にだけ,true を返す。」と決まっているので、仕様かどうかといわれれば、仕様です。
で、Autoboxingも導入されて1.0が暗黙にDoubleになっているのに気づきにくい状況でこの仕様はまずいんじゃないか、という事なら、なんとなく同情できますが、今まで偽だったnew Integer(1).equals(new Double(1.0))を真にする、という仕様変更はあり得ないと思います。
そもそも浮動小数点を==で比較しちゃだめ、っていうのは数値計算の基本だし、これはこういうものでいいのではないでしょうか。
Integerクラスのequalsメソッドはオブジェクトの比較を行うからです。「1.0」はIntegerではないのでfalseを返します。
http://java.sun.com/j2se/1.5.0/docs/api/java/lang/Integer.html#e...(java.lang.Object)
該当する部分のJavaのソースを読んだのでそれはわかっているのですが「そういう実装で本当にいいのか?」と思った次第です。
はい。
しかし、そのリンク先で述べられている通り
「==はオブジェクトの同一性の判定」「equalsは値が同じかどうかの判定」とすると
x == yの時、xとyは同じ物なのだからx.equals(y)が成り立つと考えてしまいがちです。
今回のケースはリテラルの1.0がauto-boxingされることがきっかけで、文字列の比較とはまったく逆の「==がtrueなのにequalsがfalse」という現象が起きています。
これを「従来の教科書のほとんどが間違っている」と主張すべきか「Javaの実装が変」と主張すべきかがよくわかりません。個人的には現状のInteger#equalsの実装がおかしいだけのように思います。
==では二項数値昇格が起こるのにInteger.equalsの実装はそうなっていない、これはバグじゃないか、という話ですよね。
Integer#equals()の仕様は「実引数が null でなく,このIntegerオブジェクトと同じ int 値を表現するIntegerオブジェクトである場合に,またその場合にだけ,true を返す。」と決まっているので、仕様かどうかといわれれば、仕様です。
で、Autoboxingも導入されて1.0が暗黙にDoubleになっているのに気づきにくい状況でこの仕様はまずいんじゃないか、という事なら、なんとなく同情できますが、今まで偽だったnew Integer(1).equals(new Double(1.0))を真にする、という仕様変更はあり得ないと思います。
そもそも浮動小数点を==で比較しちゃだめ、っていうのは数値計算の基本だし、これはこういうものでいいのではないでしょうか。
なるほど。
つまり「==はオブジェクトの同一性の判定」「equalsは値が同じかどうかの判定」という理解をしていると不自然に感じてはしまうけども、現にそういう仕様だし、仕様が変更されることも考えにくい、ということですね?
Javaでは過去の経緯(主に実行速度の観点)からプリミティブ型と呼ばれる型が言語仕様として残ってしまいました。
オブジェクト指向言語であるならば、「プリミティブ型と呼ばれる特殊な型は存在せずに、全てIntegerクラスのインスタンスとして管理されるべきである」と、Java否定派の方が好んで持ち出す話です。
実際にC#では完全なオブジェクト指向に近づける為、プリミティブ型を採用せずに全てInt32のようなクラスのインスタンスとして解釈されます。
いわゆるJava5のauto-boxingが働いているわけです。
とは言っても、C#でも実行速度の点では考慮しており、クラスでありプリミティブ型のような扱いを内部で行う為に構造体クラスという特殊なクラスが用意されています。
さて、質問の内容ですが、バグか?という問いにはNOとなります。
どうして挙動が変わるかに関してはauto-boxingやequalsと==の違いを理解しているようなので改めて説明する必要はないでしょう。
flashrod氏も触れているように、言語仕様としては正しいが運用上の問題を内包してしまったと捉えるのが妥当なのではないでしょうか?
経緯と問題を知った上で、現代版Effective Javaを作るのであれば「項番:XX auto-boxingに注意して比較演算子を使う」というようなTipsとなる感じがします。
よって「Javaのバグではなく、質問者のコードがバグである」というのが私の回答です。
http://www.nextindex.net/java/equals.html
java関数equals()とは、文字列が同じかどうかを判定する関数です。よってInteger x = 1;ですから、1と1.0は数値としては同じなので、System.out.println(x == 1.0); // trueとなりますが、文字列としては異なるので、System.out.println(x.equals(1.0)); // falseとなります。もし、これをtrueにしたいのであれば、equalの代わりに、equalIgnoreCase関数をお試しください。
よろしければ、下記アドレスもご覧ください。
その解釈は正しくないと思いますし、
太極拳のページはこの質問には無関係かと思います。
多分もう理解されているのでしょうが、これは == と equalsメソッドの問題というよりも boxing の問題として認識した方がスッキリしますね。
見た目のコードではなくて、
Integer x = new Integer(1); System.out.println(x.intValue() == 1.0); // true System.out.println(x.equals(new Float(1.0))); // false
というソースコードをここでは考えるべきなのでしょう。(正確にautoboxing/unboxingの仕様を調べていないのでこれが正解かどうかは保証できません。すみません)
この様にしてみると == と equalsメソッドの関係に対して、バグじゃないのか? という風には見えません。
「autoboxing/unboxing の仕様」と、「== と equalsメソッド」との間に、「ちょっとそれはまずいんじゃないの?」とか「こうやって脳内変換して考えなきゃならないのはよくないのでは?」ということに関しては、確かにこりゃ紛らわしいなぁ、とは思いました。
蛇足
IntegerとFloatが、Numberを共通の親クラスにしていて、かつ intValue()メソッドはNumberで定義されているのに、Integer#equals(Object) が true になるのが Integer クラスを引数にした時だけ、というのも気になりましたが。でもこの辺は人それぞれで考え方は違うでしょうね……。
そうですね、Integer#equalsの中で「obj instanceof Integer」とやっているところを「obj instanceof Number」にすべきではないか?というのが最初の「バグじゃないか?」というセリフだったのですけども、みなさんの話を聞いているうちに「どちらの仕様を選ぶか」という問題だと思えてきました。現状の仕様にも微妙なところはありますが、変更するほどのメリットもあるわけではないので「こういう仕様だ」と納得するしかないようですね。
なるほど。
つまり「==はオブジェクトの同一性の判定」「equalsは値が同じかどうかの判定」という理解をしていると不自然に感じてはしまうけども、現にそういう仕様だし、仕様が変更されることも考えにくい、ということですね?