オートボクシング・アンボクシングの注意

Java2 SE 5.0から、基本型とラップクラスの相互変換を自動的にやってくれるようになりました。
つまり

Integer a = 5;
System.out.println(a * 10);

などとすると

Integer a = Integer.valueOf(5);
System.out.println(a.intValue() * 10);

のように変換してくれます。
そうすると、通常ラップクラスを使う場面は、ジェネリクスでの型指定とObjectへのキャストだけになります。


ところで

Integer a = 5;
Integer b = 5;
System.out.println(a == b);

はどうなるかというと、これはtrueになります。
ところが

Integer a = 130;
Integer b = 130;
System.out.println(a == b);

の場合は、falseになります。
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);
    }

これを見るとわかりますが、-128から127までは、キャッシュを使って同じ値なら同一のインスタンスを返すようになっていますが、それ以外の値は新しいインスタンスが作られます。
ShortやLongも同様に-128から127まではキャッシュを使うようになっています。
その結果、-128から127までの値をオートボクシングでラップクラスのインスタンスに変換したときは、==で正しく比較できるけど、それ以外の値では正しく比較されないということになってしまいます。


コーディング規約でラップクラスは基本的に使わないなどとして

Map<String, Integer> m = new HashMap<String, Integer>();
m.put("a", 200);
m.put("b", 200);
int a = m.get("a");
int b = m.get("b");
System.out.println(a == b);

のようにすると、ほとんどの場合回避できますが、それに慣れて

Map<String, Integer> m = new HashMap<String, Integer>();
m.put("a", 200);
m.put("b", 200);
System.out.println(m.get("a") == m.get("b"));

とやってしまうと落とし穴に落ちてしまいます。equalsメソッドを使うべき比較ですがうっかりしてしまう可能性があるのと、値によってはうまく動くところが落とし穴です。


また、当然、上記のコードで

Map<String, Integer> m = new HashMap<String, Integer>();
m.put("a", null);
m.put("b", 200);
int a = m.get("a");
int b = m.get("b");
System.out.println(a == b);

などとなっているとぬるぽになります。
こういった、ラップクラスから基本型への変換時のぬるぽにも注意が必要です。