Code Fix

上級

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  }
出力: false期待: true (コンパイルは通っています)

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  }
出力: true(127以下なのでたまたま通ります。128にすると false になります)

たまたま動く挙動に依存するのが最も危険です。ラッパー型の比較は常に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  }
出力: true(片方がintだと開封されて値比較になります)

片側がプリミティブなら==は値の比較になります。両側がラッパーのときだけ参照比較になる点が混乱の元です。

直し方: == を == にします。

この問題を解いてみる →

パターン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  }
出力: false期待: true (コンパイルは通っています)

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  }
出力: false期待: true (コンパイルは通っています)

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を超えても==がたまたま成立することがあります。

演習をはじめる

関連するエラー

広告
広告スロット(未設定)