a==b がTrueで a.equals(b) がFalseの状態を作り出す

Javaで「a==b がTrueで a.equals(b)」というへんな状態になったのでメモ。
一般的に
・a == b だったら a.equals(b)は必ず成り立って
・a.equals(b) のときには a == b とは限らないというのがよくある話ですよね。
しかしこれが逆になるへんな事象がありました。

おちついて考えれば当たり前なのですが、とりあえずへんな事象を見てみましょう。

再現するのは以下のようなとても簡単なクラスでいけます。

public class ShortTest {
    public static void main(String[] args) {
        Short s1 = 8;
        Short s2 = 16;
        Short s3 = 24;
        System.out.println(s3 == (s1 + s2));    // true
        System.out.println(s3.equals(s1 + s2)); // false
    }
}

shortのラッパークラスであるShortクラスに入れた2つの数字を足して比較しているわけですが、
なぜか答えは正しいのに==のときはTRUEでequalsで比較するとFalseが返ってきます。

ぱっと見て原理を説明できる方はちゃんとJavaのAutoBoxingと演算子の動作を理解している方ですね。
僕はいや、そんなことはないでしょっておもってしました

「==」がTrueになる理由

「==」がTrueになる理由はJava言語仕様5.6.2Chapter 5. Conversions and Contextsに書いてあります。
要約するとどちかがdouble型だったらdoubleにあわせて、floatだったらfloatにしてlongだったらlongにしてそうじゃなかったらintに変換して処理します。
なので、Short型からshortになって、それがintに変換されて比較されるので、TRUEになります。

Shortクラスのインスタンスの参照比較にならないの?というとこんなバイトコードになるのでそういう動作にはなりません。

    25: getstatic     #4                  // Field java/lang/System.out:Ljava/io/PrintStream;
    28: aload_3
    29: invokevirtual #5                  // Method java/lang/Short.shortValue:()S
    32: aload_1
    33: invokevirtual #5                  // Method java/lang/Short.shortValue:()S
    36: aload_2
    37: invokevirtual #5                  // Method java/lang/Short.shortValue:()S
    40: iadd
    41: if_icmpne     48

「.equals」がFalseになる理由

これは上記がわかれば非常に明確です。
「+」演算子でShortの足し算をすると戻りはint型になります。
そしてそれを.equalsで比較するのでAutoBoxingされてShortとInteger型の比較が行われFalseになる。
気軽に演算子を使った結果知らぬ間に型変換されていて型が違うのでFalseになったということですね。


C#だとこうならない想定だったんだけど何故か同じ事象になるぞ。。。