JavaのMap.ofにnullを入れるとNullPointerExceptionになる理由を解説する記事のアイキャッチ。

Java クイズ

JavaのMap.ofにnullを入れると例外になる理由|HashMapとの違い

AIによる要約

Map.ofはnullのキーと値を許可しないため、Map作成時点でNullPointerExceptionになります。getを呼んだ時ではありません。また、作成したMapへputやremoveもできません。HashMapはnullを許可しますが、キー未登録と登録済みnullの区別が必要になります。固定データならMap.of、値の追加・変更やnullを扱うなら要件に合う別の表現を選びます。

この記事はXへ投稿したJavaクイズの詳しい解説です。まず問題を解きたい方は、Xの元ポストを開くことができます。

新人SE
新人SE
Map.ofでAにnullを入れてからgetすると、nullが返るんじゃないんですか?
ポンコツSE
ポンコツSE
getへ到達する前に、Mapを作る時点でNullPointerExceptionになります。

Java 9以降では、少数の固定データを簡潔に作るためにMap.ofを使えます。テストデータや区分値の対応表では便利ですが、HashMapと同じ感覚でnullを渡すと、その行で処理が停止します。

特に、画面入力や設定ファイルから取得した値をそのままMap.ofへ渡すコードは、正常データでは動いても任意項目が未入力のときだけ失敗します。APIの仕様と業務上のnullの意味を分けて考えましょう。

この記事のポイント

  • Map.ofはnullのキーと値を許可しない
  • 例外はMap作成時点で発生する
  • Map.ofで作ったMapは変更できない
  • HashMapのnull許可とは設計上の意味が異なる
  • 未設定はnull以外の型や状態で表せないか検討する

まずはXのJavaクイズを確認する

Map.ofでキーAにnullを設定したとき何が起きるかを問うJavaクイズ画像。
printlnまで到達するでしょうか。
Map<String, String> values =
        Map.of("A", null);

System.out.println(values.get("A"));

答えはNullPointerExceptionです。Map.ofはキーにも値にもnullを許可せず、printlnより前の作成処理で例外になります。

Map.ofはnullを許可せず作成時にNullPointerExceptionになると説明する画像。
答えはNullPointerExceptionです。Mapを作る段階で失敗します。

元の投稿は、Xの出題ポスト回答ポストでも確認できます。

例外はgetではなくMap作成時に発生する

コードを上から追うと、最初にMap.of("A", null)が評価されます。この呼び出しがMapを返せないため、変数valuesへの代入も、後続のgetも実行されません。障害ログの行番号もMap.ofの行を指します。

String value = null;
Map<String, String> map = Map.of("A", value);
// ここでNullPointerException

『getしたらnullになった』と誤認すると、containsKeyやObjects.requireNonNullを後段へ足しても直りません。スタックトレースの先頭と評価順を確認し、例外が起きた行を正しく特定します。

Map.ofがnullを許可しない理由

Map.ofで作るMapは、作成後に内容を変更しない小さな固定Mapです。nullを禁止することで、『キーがない』と『値がnull』の曖昧さを持ち込まず、要素の存在を明確にしています。

これはすべてのMapがnullを禁止するという意味ではありません。Mapはインターフェースであり、nullを許可するか、変更できるか、スレッドセーフかは実装や生成方法によって異なります。変数型がMapでも契約は同じではありません。

Map.ofの主な特徴

  • nullキーを渡せない
  • null値を渡せない
  • put・remove・clearで変更できない
  • 同じキーを2回指定できない
  • 要素の走査順を業務仕様として当てにしない

HashMapならnullを入れられる

HashMapは1つのnullキーと複数のnull値を許可します。そのため次のコード自体は動きます。

Map<String, String> map = new HashMap<>();
map.put("A", null);

System.out.println(map.get("A")); // null
System.out.println(map.get("B")); // null

ただし、Aは登録済みnull、Bは未登録なのに、getの戻り値はどちらもnullです。区別が必要ならcontainsKeyを使います。技術的にnullを入れられることと、業務設計として入れるべきことは別です。

設定値やDTOからMap.ofを作るときの注意

固定の文字列だけでMap.ofを作る場合はnullになりません。危険なのは、外部から取得した値を直接渡す場合です。任意入力、DBのNULL、環境変数未設定、JSONの項目不足などがnullの入口になります。

Map<String, String> attributes = Map.of(
        "department", userForm.getDepartment(),
        "extension", userForm.getExtension());

内線番号が任意項目なら、未入力時に失敗します。null項目をMapから除外する、空文字へ変換する、専用DTOを使う、必須入力にするなど、仕様に合う対応を選びます。とりあえず空文字にする修正は、未設定と空文字の意味が同じ場合だけにします。

変更したいなら最初から別のMapを選ぶ

Map.ofで作った後にputするとUnsupportedOperationExceptionになります。初期値を簡潔に書きつつ後で変更したいなら、HashMapのコンストラクタへ渡して可変コピーを作ります。

Map<String, String> map = new HashMap<>(
        Map.of("status", "NEW"));

map.put("owner", "U001");

一方、固定の区分表や定数として使うなら、変更できない方が安全です。『念のためHashMap』にすると、後続処理が誤って書き換えても気付きにくくなります。ライフサイクルから可変・不変を選びます。

nullを持たせない設計を検討する

Mapの値にnullを入れると、未取得、該当なし、入力なし、取得失敗など複数の意味が混ざりやすくなります。必要ならOptional、状態を持つDTO、enum、結果クラスで意味を分けます。

record UserAttribute(String department, Optional<String> extension) {}

UserAttribute attribute = new UserAttribute(
        department, Optional.ofNullable(extension));

Optionalをフィールドに使うかはプロジェクト方針もあります。大切なのはOptionalそのものではなく、『このnullは何を意味するか』を型や契約で説明できることです。

JUnitで作成時の契約を確認する

ライブラリ仕様をそのままテストする必要はありませんが、外部入力から固定Mapを作る自作メソッドには、任意項目null時の期待動作を残します。

@Test
void 任意項目がnullならMapから除外する() {
    Map<String, String> actual = mapper.toAttributes(
            new UserForm("営業", null));

    assertEquals("営業", actual.get("department"));
    assertFalse(actual.containsKey("extension"));
}

例外にする設計なら、どの例外型とエラーコードに変換するかをテストします。NullPointerExceptionが偶然そのまま500エラーになる状態は、利用者にも運用者にも意図が伝わりません。

現場レビューでよくある指摘

Map.ofのレビューでは、短く書けているかより、入力がnullにならない契約と作成後に変更しない意図が成立するかを見ます。

// レビューコメント例
extensionは任意入力なのでnullになります。Map.ofはnullを許可しません。
未設定項目を除外するか、専用DTOで表現してください。

// レビューコメント例
このMapは後続でputしています。Map.ofの戻り値は変更できないため、
最初からHashMapを使うか、可変コピーを作ってください。

// レビューコメント例
値nullとキー未登録を区別する仕様でしょうか。
HashMapへnullを入れる前に、状態の表現方法を整理してください。

NullPointerExceptionへ場当たり的なnullチェックを足す前に、どの入力がnullになり、Mapにその状態を保持する必要があるか確認します。作成時と利用時の契約をそろえるのが先です。

提出前のセルフチェック

レビュー前に確認すること

  • Map.ofへ渡す値がnullにならないか
  • 例外発生行を作成時点として確認したか
  • 作成後にputやremoveをしていないか
  • キー未登録と値nullを区別する必要があるか
  • 固定Mapとして不変にする意図があるか
  • 同一キーや順序へ依存していないか
  • 任意入力・DB NULL・環境変数未設定をテストしたか

Javaコレクションを体系的に学ぶ参考書

Mapは生成方法ごとにnull、変更可否、順序の契約が異なります。コレクションの基本をまとまって確認すると、短いファクトリメソッドを安全に選べます。

書籍「スッキリわかるJava入門 第5版」の表紙
商品画像:Amazon.co.jp
PR Java初学者向け

スッキリわかるJava入門 第5版

中山 清喬 (著), 国本 大悟 (著), 株式会社フレアリンク (監修)

Java文法とオブジェクト指向を、最初から整理する一冊。

豊富な図とサンプルで、変数、条件分岐、クラス、例外処理まで順番に学べます。Javaの最初の教科書を探している人向けです。

  • Javaを基礎から体系的に学びたい
  • Java 25 LTS対応の入門書を選びたい

当サイトはAmazonアソシエイト・プログラムの参加者です。価格・在庫・配送条件はAmazonでご確認ください。

この記事とあわせて読みたい

まとめ

Map.ofはnullのキーと値を許可しないため、nullを渡すとMap作成時点でNullPointerExceptionになります。また、作成したMapはputやremoveで変更できません。

HashMapならnullを格納できますが、未登録との区別が必要です。入力がnullになる理由とMapのライフサイクルを確認し、不変Map、可変Map、専用DTOのどれが意図を表せるか選んでください。

-Java, クイズ
-,