ラッパークラスで値を比較するときはequals
を使う
Stringの比較でよく言われています。
比較で==
を使うと
ラッパーなどの参照型の場合は参照先を比較、プリミティブ型の場合は値を比較します。
String hoge = "hoge";
String fuga = "hogehoge";
fuga = fuga.substring(4); // << "hoge"になる
// 参照先を比較するので「false」になる
boolean isEqual1 = hoge == fuga;
// 値を比較するので「true」になる
boolean isEqual2 = hoge.equals(fuga);
System.out.print(isEqual2);
Shortを比較するのに==
を使っても「-128~127」の範囲は値を比較してくれる
うっかりShort(shortのラッパー)を==
で比較していたけど途中まで気が付かなかった、気が付いたのは値が200ぐらいになった時。へぇー知らなかった・・・とはいえequals
を使うべきですね。
Boolean isEquala = null;
// 「-128~127」の範囲外なので「false」になる
Short a1 = -129;
Short a2 = -129;
isEqual = a1 == a2;
// 「-128~127」の範囲内なので「true」になる
Short b1 = -128;
Short b2 = -128;
isEqual = b1 == b2;
// 「-128~127」の範囲内なので「true」になる
Short c1 = 127;
Short c2 = 127;
isEqual = c1 == c2;
// 「-128~127」の範囲外なので「false」になる
Short d1 = 128;
Short d2 = 128;
isEqual = d1 == d2;
たぶん、Integerと同じ理由だと思われます。
intからIntegerへは「new Integer()」でなく「Integer.valueOf()」が使われるから。
これは(現状では)-128~127の範囲ではキャッシュされたIntegerを返すようになっているので、その範囲なら同一のインスタンスとなる。だから==で比較しても等しくなる。
Java型メモ(Hishidama's Java type Memo)
Shortの比較で==
を使っていてもNullPointerExceptionになる
意外だった・・・というか納得できない
Short a = null;
Boolean isOne = null;
// 想像通りにNullPointerExceptionになる
isOne = a.equals(1);
// 想像通りに「true」になる
isOne = a == null;
// 意外にNullPointerExceptionになる!
isOne = a == 1;
// これはNullPointerExceptionにならずに「false」になる
isOne = a == new Short("1");
// これも「false」になる
Integer i = 1;
isOne = a == new Short(i.shortValue());
プリミティブ型とラッパークラスを自動変換するボクシング変換というものがある
JDK1.5からできるようになったそうで、そんな名前があることを知らなかった。
// ラッパークラスに普通の代入
Short a1 = new Short("1000");
// オートボクシング:プリミティブ型をラッパークラスに自動変換
Short a2 = 1000; // classファイルを逆コンパイルするとこうなる >> Short a2 = Short.valueOf((short)1000);
// プリミティブ型に普通の代入
short b1 = a1.shortValue();
// アンボクシング:ラッパークラスをプリミティブ型に自動変換
short b2 = a2; // classファイルを逆コンパイルするとこうなる >> short b2 = a2.shortValue();
ボクシング変換されているかを確かめたくてShortの比較で==
を使ったところを逆コンパイルしてみた
ボクシング変換されているかは逆コンパイルするとわかるらしい、だから逆コンパイルしてみた。
コンパイルしたものを逆コンパイルして見ると、ただ単に変換メソッドに置き換えられているだけ…。
Java型メモ(Hishidama's Java type Memo)
isOne = a.equals(1);
// ↑これをコンパイルして逆コンパイルするとこうなる↓
isOne = Boolean.valueOf(a.equals(Integer.valueOf(1)));
isOne = a == null;
// ↑これをコンパイルして逆コンパイルするとこうなる↓
isOne = Boolean.valueOf(a == null);
isOne = a == 1;
// ↑これをコンパイルして逆コンパイルするとこうなる↓
isOne = Boolean.valueOf(a.shortValue() == 1);
isOne = a == new Short("1");
// ↑これをコンパイルして逆コンパイルするとこうなる↓
isOne = Boolean.valueOf(a == new Short("1"));
Integer i = 1;
isOne = a == new Short(i.shortValue());
// ↑これをコンパイルして逆コンパイルするとこうなる↓
Integer i = Integer.valueOf(1);
isOne = Boolean.valueOf(a == new Short(i.shortValue()));
Shortをプリミティブ型と==
で比較するとshortValue()
が実行されるからNullPointerExceptionになる!
a == 1;
// ↑は、こう↓なる
Boolean.valueOf(a.shortValue() == 1);
NullPointerExceptionになる理由は分かったけどなんでリテラルの数値の時だけshortValue()
なんだろう?
a == new Short("1");
は、Boolean.valueOf(a.shortValue() == new Short(i.shortValue()));
にならないんだろう?
そんな疑問に @swordone さんが教えてくれました。
ラッパークラス(参照型)をプリミティブ型と比較するとオートボクシングが行われるので、
比較対象が参照型であれば.shortValue()
は実行されずNullPointerExceptionは発生しない。
// 例えばこれは、比較対象がプリミティブ型なので
boolean isOne = a == (short) 1;
// オートボクシングが実行される(↓逆コンパイルした結果)
boolean isOne = a.shortValue() == 1;