Code Fix

上級

オートボクシングで起きる NullPointerException

Integerはnullを持てますが、演算時にintへ開封されます。Map.getの戻り値の受け方を扱います。

エラーメッセージの読み方

Exception in thread "main" java.lang.NullPointerException
main
スレッド名 — mainなら起動直後の処理で発生しています
java.lang
パッケージ — java.lang なら標準の基本例外です
NullPointerException
例外クラス — 何が起きたか。ここを検索するのが最短です

このエラーが出る典型パターン

パターン1

 1  public class Main {
 2      public static void main(String[] args) {
 3          Integer n = null;
               ^
 4          System.out.println(n + 1);
 5      }
 6  }
Exception in thread "main" java.lang.NullPointerExceptionexited with code 1

Integerはnullを持てますが、演算時にintへ自動開封されます。nullの開封はNullPointerExceptionです。

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

この問題を解いてみる →

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

パターン2

 1  import java.util.HashMap;
 2  import java.util.Map;
 3  
 4  public class Main {
 5      public static void main(String[] args) {
 6          Map<String, Integer> m = new HashMap<>();
 7          int     v = m.get("missing");
               ^
 8          System.out.println(v);
 9      }
10  }
Exception in thread "main" java.lang.NullPointerExceptionexited with code 1

getはキーがなければnullを返します。intで受けると開封が走って落ちるため、Integerで受けるか既定値を使います。

直し方: int を Integer にします。

この問題を解いてみる →

パターン3

 1  import java.util.List;
 2  
 3  public class Main {
 4      public static void main(String[] args) {
 5          List<Integer> list = List.of(10, 20, 30);
 6          list.remove(Integer.valueOf(1));
                                 ^
 7          System.out.println(list);
 8      }
 9  }
remove(1) は添字1の要素を消し、remove(Integer.valueOf(1)) は値1を探して消す(どちらもコンパイルは通ります)

Listのremoveはintとオブジェクトで意味が変わる数少ない例です。意図をキャストで明示します。

直し方: Integer.valueOf(1) を Integer.valueOf(1) にします。

この問題を解いてみる →

パターン4

 1  public class Main {
 2      public static void main(String[] args) {
 3          Integer a = null;
 4          boolean condition = false;
 5          int result = condition ? 1 : a           ;
                                               ^
 6          System.out.println(result);
 7      }
 8  }
Exception in thread "main" java.lang.NullPointerExceptionexited with code 1

三項演算子の結果をintで受けると、選ばれた側のIntegerがnullなら自動開封の瞬間に例外になります。

直し方: a を 0 にします。

この問題を解いてみる →

パターン5

 1  public class Main {
 2      public static void main(String[] args) {
 3          Integer[] nums = new Integer[3]        ;
                                        ^
 4          int sum = 0;
 5          for (Integer n : nums) sum += n;
 6          System.out.println(sum);
 7      }
 8  }
Exception in thread "main" java.lang.NullPointerExceptionexited with code 1

Integer配列の要素の既定値はnullです(intのように0にはなりません)。合計する前に必ず値を入れます。

直し方: new Integer[3] を new Integer[]{1, 2, 3} にします。

この問題を解いてみる →

よくある誤解

NullPointerExceptionなのにnullを書いた記憶がない場合、自動開封を疑ってください。ラッパー型がintに変換される瞬間に起きます。

実務での勘所

オートボクシングによるNullPointerExceptionは特殊な例外ではなく、コンパイラがInteger.intValue()という通常のメソッド呼び出しを自動的に挿入しているだけです。int x = map.get("missing");のようなコードは、実際にはint x = map.get("missing").intValue();とほぼ同じ形にコンパイルされます。JDK 14以降の詳細なエラーメッセージでは「Cannot invoke "java.lang.Integer.intValue()" because ... is null」のように、自分では書いていないintValue()の呼び出しがそのまま表示されるため、これを知っていれば見覚えのないメソッド名への戸惑いがなくなります。

演習をはじめる

関連するエラー

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