Integer の == が 127 までしか動かない理由
-128〜127のキャッシュが原因です。たまたま動く挙動に依存する危険性を扱います。
なぜエラーが出ないのか
出力: false
(エラーなし)- javacは何も報告しません。文法として正しいためです
出力: false- 実際の挙動 — 期待した結果と食い違っている箇所
見つけ方- エラーが出ないので、出力を目で確かめるしかありません。この種の誤りが最も発見が遅れます
このエラーが出る典型パターン
パターン1
1 public class Main { 2 public static void main(String[] args) { 3 Integer a = 128, b = 128; 4 System.out.println(a == b ); ^ 5 } 6 }
Integerは-128〜127だけキャッシュされ同一実体になります。128以上は別実体なので==がfalseになります。
直し方: a == b を a.equals(b) にします。
パターン2
1 public class Main { 2 public static void main(String[] args) { 3 Integer a = 100, b = 100; 4 System.out.println(a == b); ^ 5 } 6 }
たまたま動く挙動に依存するのが最も危険です。ラッパー型の比較は常にequalsを使います。
直し方: == を == にします。
パターン3
1 public class Main { 2 public static void main(String[] args) { 3 Integer a = 1000; 4 int b = 1000; 5 System.out.println(a == b); ^ 6 } 7 }
片側がプリミティブなら==は値の比較になります。両側がラッパーのときだけ参照比較になる点が混乱の元です。
直し方: == を == にします。
パターン4
1 public class Main { 2 @SuppressWarnings("deprecation") 3 public static void main(String[] args) { 4 Integer a = new Integer(100) ; ^ 5 Integer b = 100; 6 System.out.println(a == b); 7 } 8 }
newは常にキャッシュを無視して新しいオブジェクトを作ります。キャッシュされた実体と比較するにはvalueOfかオートボクシングを使います。
直し方: new Integer(100) を Integer.valueOf(100) にします。
パターン5
1 public class Main { 2 public static void main(String[] args) { 3 Long a = 200L, b = 200L; 4 System.out.println(a == b ); ^ 5 } 6 }
Long も Integer と同じく -128〜127 だけキャッシュされます。200は範囲外なので==は参照比較になり別実体としてfalseになります。
直し方: a == b を a.equals(b) にします。
よくある誤解
127以下でテストすると通ってしまいます。テストデータの選び方次第で、本番まで見逃します。
実務での勘所
このキャッシュが働くのは、オートボクシングがInteger.valueOf(int)を経由する場合だけです。廃止予定のnew Integer(x)という書き方(Java 9以降非推奨)は、キャッシュを無視して毎回新しいオブジェクトを作るため、-128〜127の範囲でも==はfalseになります。JLSはこのキャッシュの範囲を「最低でも-128〜127」と定めているだけなので、HotSpot VMには-XX:AutoBoxCacheMaxでキャッシュの上限を広げるオプションがあり、実行環境のJVM設定次第では127を超えても==がたまたま成立することがあります。