- JAVA
- 技術 💻
Java NullPointerExceptionの原因と対処
NullPointerException(NPE)は、「中身が空(null)の参照に対して、メソッド呼び出しやフィールドアクセスをしようとした」ときにJavaが投げる例外です。Java入門者が最初に出会う例外であり、経験者でも本番障害の常連です。nullを考案した本人が後年「10億ドルの過ち」と振り返ったほど、この問題は根深いものです。
この記事では、エラーメッセージの読み方(Java 14以降はメッセージがnullだった変数を教えてくれます)、よくある原因の5パターン、そしてそもそもNPEが起きない設計——Optional・record・アノテーション——までを、一本で通して解説します。
まず、エラーメッセージを読む
NPEの調査は、スタックトレースを読むところから始まります。
Exception in thread "main" java.lang.NullPointerException:
Cannot invoke "String.length()" because "user.getName()" is null
at com.example.UserService.validate(UserService.java:42)
at com.example.UserService.register(UserService.java:20)
読み方は3段階です。
- 1行目の例外名とメッセージ — 何が起きたか。上の例では「
user.getName()がnullだったのでString.length()を呼べなかった」と、nullだった式そのものが書かれています atで始まる行の、自分のパッケージが最初に出てくる行 — 事故現場。UserService.javaの42行目です- その下の行 — そこに至った呼び出し経路
Java 14以降は「何がnullだったか」を教えてくれる
Java 14で導入された Helpful NullPointerExceptions により、上の例のように「どの式がnullだったか」がメッセージに含まれるようになりました(Java 15以降は既定で有効)。
古いJavaでは「NullPointerException」とだけ出て、1行に複数の呼び出しがあると「どれがnullか」を自分で切り分ける必要がありました。メッセージに式が出ていない場合は、まずJavaのバージョンを確認してください。
よくある原因5パターン
1. 初期化していないフィールド
private List<String> items; // 宣言だけ。newしていない
public void add(String s) { items.add(s); } // NPE
対処:宣言時に初期化する(= new ArrayList<>())か、コンストラクタで必ず代入する。final を付けると、代入忘れをコンパイラが検出してくれます。
2. メソッドの戻り値がnull
User user = repository.findById(id); // 見つからないとnullを返す
user.getName(); // NPE
対処:戻り値がnullになり得るメソッドかを、呼び出す側が確認する。根本対策は、後述のOptionalで「無いかもしれない」を型で表現することです。
3. Mapのgetで存在しないキー
Map<String, Integer> stock = ...;
int count = stock.get("apple"); // キーが無いとnull → intへの変換でNPE
対処:getOrDefault("apple", 0) を使う、または containsKey で確認する。プリミティブ型への自動変換(アンボクシング)がNPEの発生源になる、見落としやすいパターンです。
4. 文字列の比較で、変数側を左に書いている
if (status.equals("ACTIVE")) { ... } // statusがnullならNPE
対処:定数を左に書く("ACTIVE".equals(status))。これならstatusがnullでも false が返ります。Objects.equals(a, b) を使えば、どちらがnullでも安全です。
5. 外部から来たデータ(JSON・DB・リクエスト)の項目が欠けている
String zip = request.getAddress().getZipCode(); // addressが無いとNPE
対処:境界(APIの入口・DBからの取得)で必須項目を検証する。「後続の処理は、nullが来ない前提で書く」と決めて、入口で守るのが定石です。
対症療法:nullチェックの書き方
if (user != null && user.getName() != null) {
...
}
これで例外は防げますが、コードが != null だらけになり、「本当にnullが来るのか」「来たら何が正しいのか」が読めなくなります。nullチェックは最後の手段にして、次の設計で「nullを作らない」方向に寄せるのが現代の書き方です。
予防設計1:Optionalで「無いかもしれない」を型で表す
// 戻り値に Optional を使う
public Optional<User> findById(String id) { ... }
// 呼び出し側は、無い場合の扱いを必ず書くことになる
String name = repository.findById(id)
.map(User::getName)
.orElse("(未登録)");
Optionalは、「値が無いかもしれない」ことを戻り値の型で宣言する仕組みです。呼び出す側は map / orElse / orElseThrow などで「無いときどうするか」を書かざるを得ないので、nullチェックの書き忘れが構造的に減ります。
使いどころの注意:Optionalは戻り値のためのものです。フィールドやメソッド引数にOptionalを使うのは推奨されていません。また、optional.get() を確認なしで呼ぶと結局NPE相当(NoSuchElementException)になるので、get() は避けて orElse 系を使います。
予防設計2:recordとfinalで「作った瞬間に揃っている」状態にする
public record Address(String zipCode, String city) {
public Address {
Objects.requireNonNull(zipCode, "zipCode");
Objects.requireNonNull(city, "city");
}
}
recordはフィールドが不変で、コンストラクタで検証すれば「不完全なオブジェクトが存在できない」状態になります。原因1(初期化忘れ)は、この設計で消えます。Objects.requireNonNull は、nullが渡された時点で、分かりやすいメッセージ付きで例外を出してくれるので、原因の特定が早くなります。
予防設計3:アノテーションで「nullを許すか」を明示する
@NonNull / @Nullable 系のアノテーション(フレームワークやツールが提供)をパラメータや戻り値に付けると、IDEや静的解析ツールがnullの受け渡しを警告してくれます。
「このメソッドはnullを返すことがある」をコードの外の知識ではなく、コードの上に書く。チーム開発では、この明示が一番効きます。
予防設計4:境界で守り、内側はnullフリーにする
もっとも効果が大きい方針です。
- 入口(APIリクエスト・DB取得・ファイル読み込み)で必須項目を検証する
- 検証を通ったデータは、nullを含まない前提でドメインの処理を書く
- 「空」を表現したいときはnullではなく、空リスト・空文字・Optionalを使う
「nullは境界までで止める」と決めると、内側のコードから != null が消え、読みやすさと安全性が同時に上がります。
レビューで見る観点
- 戻り値にnullを返しているメソッドはないか(Optionalか空コレクションに置き換えられないか)
.get()の直前に存在確認がないOptionalはないか- 文字列比較で変数が左に来ていないか
- 外部から来たオブジェクトを、検証なしでドット連鎖(
a.getB().getC())していないか Map.getの結果をプリミティブに代入していないか
よくある質問
Q. 「ぬるぽ」とNullPointerExceptionは同じですか? A. 同じです。NullPointerExceptionの略称として、日本のエンジニアの間で古くから使われている俗称です。
Q. nullチェックを全部に書けば安全ですか? A. 例外は防げますが、「なぜnullが来るのか」を放置したままになり、本当の不具合を隠すことがあります。まず「nullを作らない設計」に寄せ、チェックは境界に集中させるのが定石です。
Q. Optionalを使うと遅くなりませんか? A. オブジェクトを1つ余分に作るコストはありますが、業務システムの大半では無視できる範囲です。極端に呼び出し回数が多いホットスポットでのみ、計測して判断してください。
Q. Kotlinのようにnull安全な言語に移れば解決しますか? A. 言語レベルのnull安全は強力ですが、Javaでも「Optional・record・境界での検証」で同等の安全性に近づけます。移行の可否は別として、設計の原則は同じです。
まとめ
NullPointerExceptionは、「無いかもしれない」を曖昧にしたまま進めたときに起きる例外です。
- 調査は、メッセージの式名(Java 14+)と、自分のパッケージの最初の行から
- 原因の大半は、初期化忘れ・null返却・Map.get・equalsの向き・外部データの欠落
- 予防は、Optionalで型に表す/recordで不完全な状態を作らない/境界で検証して内側をnullフリーにする
株式会社RJCの研修は、Javaの基礎からAI駆動開発まで4ヶ月・12レベルで進みますが、NPEとの付き合い方は最初期から扱います。エラーを読める人ほど、AIを含めた道具をうまく使える——その順番を大切にしています。
WE ARE HIRING
RJCで一緒に開発しながら、
成長を楽しみませんか?
RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!
- 127日年間休日
- 12時間平均残業時間
- 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!
ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。