読者です 読者をやめる 読者になる 読者になる

ボクシングされたオブジェクトの == 比較にだまされるかも


Java5 からボクシングとアンボクシングが自動で処理されるようになり便利になりました。しかしオートボクシングの分かりにくい挙動もあります。


Integer と int の比較を以下のようにすると、

Integer i1 = 100;
int i2 = 100;
if ( i1 == i2 ) {
    System.out.println("i1 == i2");
} else {
    System.out.println("i1 != i2");
}

以下の結果となり、等値での比較が行われます。

i1 == i2


アンボクシングされて i1.intValue() のようなコードと解釈されるため

Integer i1 = Integer.valueOf(100);
int i2 = 100;
if (i1.intValue() == i2) {
    System.out.println("i1 == i2");
} else {
    System.out.println("i1 != i2");
}

int 値に対する == 比較なので当然 i1 == i2 となる訳です。

そして2つの Integer を比較すると

i2 を Integer にしてみると、

Integer i1 = 100;
Integer i2 = 100;	
if ( i1 == i2 ) {
    System.out.println("i1 == i2");
} else {
    System.out.println("i1 != i2");
}

以下のような結果となります。

i1 == i2


なるほど、オートアンボクシングにより今や数値オブジェクトに equals を使う必要がなくなった訳です。

と、ちょっと待ってください・

数値を200に変えて比較すると、

Integer i1 = 200;
Integer i2 = 200;	
if ( i1 == i2 ) {
    System.out.println("i1 == i2");
} else {
    System.out.println("i1 != i2");
}

あれれ、だめですネ。

i1 != i2


上のコードは以下のように扱われます。

Integer i = Integer.valueOf(200);
Integer i2 = Integer.valueOf(200);
if ( i1 == i2 ) {
    System.out.println("i1 == i2");
} else {
    System.out.println("i1 != i2");
}

つまりオブジェクト同士の等価性の判定になっている訳です。


Integer i1 = 100; の場合はなぜ大丈夫だったかと言うと、Integer.valueOf() の結果は不変オブジェクトとしてキャッシュされて再利用されるためです。実際 Integer.valueOf() は以下のようなコードとなっています。

public static Integer valueOf(int i) {
    final int offset = 128;
    if (i >= -128 && i <= 127) { // must cache 
        return IntegerCache.cache[i + offset];
    }
    return new Integer(i);
}

まとめると

  • boolean の true と false
  • byte 値の全て
  • shot と int -128 から 127
  • char の \u0000 から \u007F

は不変オブジェクトによるキャッシュが再利用される。
アンボクシングにより == 比較が有効になったように見えることがありますが、だまされてはいけません。