ラッパーとオートボクシング
ラッパーとオートボクシング
このレッスンでできるようになること。Integer どうしを == で比べたときに true になる場合と false になる場合を、値を見て言い当てられるようになります。Silver で最も有名な引っ掛けのひとつです。
まず予測してください
次の4行はそれぞれ何を表示するでしょうか。
Integer a = 127;
Integer b = 127;
Integer c = 128;
Integer d = 128;
System.out.println(a == b);
System.out.println(c == d);
System.out.println(a.equals(b));
System.out.println(c.equals(d));答えと理由
上から true、false、true、true です。値が 1 違うだけで結果が変わります。
Integer は参照型なので、== は「同じ値か」ではなく「同じオブジェクトか」を見ます。それなのに 127 で true になるのは、Java が -128 から 127 までの Integer をあらかじめ作り置きしているからです。この作り置きを Integer キャッシュと呼びます。
Integer a = 127; と書くと、内部では Integer.valueOf(127) が呼ばれます。valueOf はキャッシュの範囲内なら作り置きのオブジェクトをそのまま返すので、a と b はまったく同じオブジェクトを指します。だから == が true になります。
128 はキャッシュの範囲外です。valueOf は毎回 new して新しいオブジェクトを返すので、c と d は別物になり == は false です。値は等しいのに false というのがこの罠の核心です。
equals は中身の値を比べるので、どちらも true になります。数値として等しいか知りたいときは常に equals を使う、というのが実務の答えです。
キャッシュされる範囲
作り置きがあるのは次のとおりです。
- Boolean は true と false の両方
- Byte、Short、Integer、Long は -128 から 127
- Character は 0 から 127
- Float と Double はキャッシュされません
つまり Double x = 1.0; Double y = 1.0; x == y; は必ず false です。「小さい値だから true」と読ませる選択肢が並びますが、小数のラッパーには作り置きがありません。
片方がプリミティブなら値の比較になります
Integer c = 128;
int e = 128;
System.out.println(c == e); // true== の片側がプリミティブのときは、参照の比較にはなりません。Integer 側が自動的に int に開かれて(アンボクシング)、数値として比べられます。だからキャッシュの範囲に関係なく true です。Integer どうしなら参照の比較、片方が int なら値の比較という切り替わりを覚えてください。
型が違う equals は必ず false
Long g = 127L;
System.out.println(g.equals(127)); // false
System.out.println(g.equals(127L)); // trueequals は最初に型を確かめます。g.equals(127) の 127 は Integer に箱詰めされるので、Long と Integer で型が違い false になります。値は同じなのに false になる、もうひとつの罠です。
null のアンボクシングは例外になります
Integer n = null; int m = n; は NullPointerException です。コンパイルは通るので、実行するまで気付けません。ラッパーは null を持てるのに、プリミティブは持てないためです。if (n == 0) のような比較も、n が null なら同じ理由で落ちます。
まとめ
-128 から 127 までは作り置きなので == が true、それを超えると false、片方がプリミティブなら値の比較、型の違う equals は false、null のアンボクシングは実行時に落ちます。この5点で出題は網羅できます。
要件
- 127 を代入した Integer 変数を2つ、128 を代入した Integer 変数を2つ用意すること
- int 型の変数にも 128 を代入し、Integer と == で比べること
- Long 型の変数に 127L を代入し、equals に int の 127 を渡すこと
- 5つの真偽値を半角スペース1つでつないだ文字列を返すこと
入出力例
compare() → "true false true true false"
compare() → "true false true true false"