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. 1行目の例外名とメッセージ — 何が起きたか。上の例では「user.getName() がnullだったので String.length() を呼べなかった」と、nullだった式そのものが書かれています
  2. at で始まる行の、自分のパッケージが最初に出てくる行 — 事故現場。UserService.java の42行目です
  3. その下の行 — そこに至った呼び出し経路

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期連続の黒字決算。

ABOUT RJC RJCがどんな会社か知る 考え方、研修、働き方、福利厚生、社員の前職まで。RJCのことが丸わかり! RJC丸わかりページへ JOB DESCRIPTION 仕事内容・待遇を見てみる 仕事内容、給与・待遇、選考の流れ。経験者も未経験も!応募前に知りたいこと、まとめました! 募集要項を見る ENTRY エントリーする エントリーは1〜2分・履歴書不要です。まずは話を聞いてみたい、という方でも歓迎です! エントリーフォームへ

RJCで一緒に開発しながら、 成長を楽しみませんか?