- JAVA
Java nullチェックの書き方|requireNonNullとisNull
Javaのnullチェックは、判定するだけなら if (x == null)、nullを受け付けないと決めるなら Objects.requireNonNull(x, "メッセージ")、nullのときに代わりの値を使うなら Objects.requireNonNullElse(x, 既定値) と書きます。 Objects.isNull と Objects.nonNull は == null と同じ結果を返すメソッドで、主に Stream の filter(Objects::nonNull) のようなメソッド参照で使います。
この記事では、6つの書き方を 「用途・nullのときの動作・導入バージョン」で比較した早見表を最初に置き、そのあとで OpenJDK 21.0.10 で動かした出力を1つずつ載せています。requireNonNull のメッセージ付き例外、Java 14 以降で読みやすくなった NullPointerException のメッセージ、文字列の null・空文字・空白の判定パターンまで、そのまま貼って使える形でまとめました。
nullチェックの早見表
| 書き方 | 用途 | null のときの動作 | 導入バージョン |
|---|---|---|---|
x == null / x != null |
その場で分岐する | true / false を返す |
最初から |
Objects.isNull(x) |
メソッド参照で「nullか」を渡す | true を返す |
Java 8 |
Objects.nonNull(x) |
filter(Objects::nonNull) で null を除く |
false を返す |
Java 8 |
Objects.requireNonNull(x) |
null を受け付けないと決める | NullPointerException を投げる | Java 7 |
Objects.requireNonNull(x, "説明") |
上と同じ。原因がわかる文を付ける | 指定したメッセージ付きの NullPointerException | Java 7 |
Objects.requireNonNull(x, () -> "説明") |
メッセージの組み立てを null のときだけ行う | Supplier が作ったメッセージ付きの例外 | Java 8 |
Objects.requireNonNullElse(x, 既定値) |
null なら代わりの値を使う | 既定値を返す(既定値も null なら例外) | Java 9 |
Objects.requireNonNullElseGet(x, () -> 既定値) |
既定値を作る処理が重いとき | Supplier の結果を返す | Java 9 |
Optional.ofNullable(x) |
戻り値で「値がないかもしれない」を伝える | 空の Optional になる | Java 8 |
Objects.equals(a, b) |
null を含むかもしれない2つを比べる | 両方 null なら true、片方だけなら false |
Java 7 |
Objects.toString(x, "未設定") |
表示用の文字列にする | 第2引数の文字列を返す | Java 7 |
導入バージョンは、今回 Java 21 の java.util.Objects のAPIドキュメント(英語版)を開いて、各メソッドの Since 欄で確認しました。
どれを使うかは、「nullだったときにどうしたいか」で決まります。
| nullだったときにしたいこと | 選ぶ書き方 |
|---|---|
| 処理を分けたい | if (x == null) { ... } |
| 呼び出し側に知らせて止めたい | Objects.requireNonNull(x, "x は必須です") |
| 決まった値で続けたい | Objects.requireNonNullElse(x, 既定値) |
| リストから取り除きたい | stream.filter(Objects::nonNull) |
| 呼び出し側に判断を任せたい | 戻り値を Optional<T> にする |
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「Java nullチェックの書き方|requireNonNullとisNull」
検証した環境
| 項目 | 内容 |
|---|---|
| JDK | OpenJDK 21.0.10 |
| 実行方法 | java ファイル名.java(単一ファイル実行)。一部は javac でコンパイルしてから実行 |
| 外部ライブラリ | 使っていません(標準ライブラリのみ) |
Apache Commons Lang の StringUtils など外部ライブラリの判定メソッドは、今回は動かしていません(未実行)。この記事のコードは、JDK だけで動きます。
== null で判定する基本の書き方
nullかどうかを調べるいちばん基本の書き方は == と != です。 equals は使いません。null に対して x.equals(...) を呼ぶと、その呼び出し自体が NullPointerException になるためです。
public class Basic {
public static void main(String[] args) {
String name = null;
String city = "Tokyo";
System.out.println(name == null); // nullか
System.out.println(name != null); // nullでないか
if (city != null) {
System.out.println(city.length());
}
// 三項演算子で代わりの値を使う
String label = (name != null) ? name : "名無し";
System.out.println(label);
}
}
実行結果:
true
false
5
名無し
&& と || で「nullでなければ続きを評価する」
&& は左側が false なら右側を評価せず、|| は左側が true なら右側を評価しません。この性質を使うと、nullチェックとそのあとの判定を1行にまとめられます。
// name が null でなく、かつ空でもない
if (name != null && !name.isEmpty()) { ... }
// name が null、または空
if (name == null || name.isEmpty()) { ... }
nullチェックを左側に書くことが大切です。 順番を逆にして name.isEmpty() || name == null と書くと、先に name.isEmpty() が評価されて例外になります。
instanceof は null のとき false を返す
instanceof は、左側が null のとき常に false を返します。そのため、型の判定と nullチェックを同時に行えます。
Object o = null;
System.out.println(o instanceof String); // false
// Java 16 以降:型の判定・nullチェック・変数への代入を1行で
if (o instanceof String s) {
System.out.println(s.length());
}
1行目は実際に false と表示されました。o instanceof String s の形(パターンマッチング)は Java 16 から使えます。
Objects.isNull と Objects.nonNull の使い方
Objects.isNull(x) は x == null と、Objects.nonNull(x) は x != null と同じ結果を返します。
import java.util.List;
import java.util.Objects;
import java.util.stream.Stream;
public class IsNullDemo {
public static void main(String[] args) {
String name = null;
String city = "Tokyo";
System.out.println(Objects.isNull(name));
System.out.println(Objects.nonNull(name));
System.out.println(Objects.isNull(city));
System.out.println(Objects.nonNull(city));
// null を取り除く
List<String> list = Stream.of("a", null, "b", null)
.filter(Objects::nonNull)
.toList();
System.out.println(list);
}
}
実行結果:
true
false
false
true
[a, b]
このメソッドがいちばん役に立つのは、メソッド参照として渡す場面です。filter(x -> x != null) と書いても同じ結果ですが、filter(Objects::nonNull) のほうが「null でないものを残す」という意図が名前で伝わります。
| 場面 | おすすめの書き方 | 理由 |
|---|---|---|
| if 文の条件 | if (x == null) |
短く、どの Java バージョンでも同じ |
| Stream の filter | .filter(Objects::nonNull) |
メソッド参照で意図が読み取りやすい |
Predicate を引数に渡す |
Objects::isNull |
ラムダ式を書かずに済む |
removeIf で null を消す |
list.removeIf(Objects::isNull) |
同上 |
if 文の中で Objects.isNull(x) を使っても動作は同じです。チーム内でどちらかにそろえておくと、読むときに迷いません。なお、toList() は Java 16 以降のメソッドで、それより前のバージョンでは collect(Collectors.toList()) を使います。
Objects.requireNonNull で null を受け付けない
Objects.requireNonNull は、「この値は null であってはならない」という前提を、コードの入口で確かめるためのメソッドです。null でなければ渡した値をそのまま返し、null なら NullPointerException を投げます。
import java.util.Objects;
public class RequireDemo {
public static void main(String[] args) {
String name = null;
try {
Objects.requireNonNull(name);
} catch (NullPointerException e) {
System.out.println("A: " + e.getMessage());
}
try {
Objects.requireNonNull(name, "name は必須です");
} catch (NullPointerException e) {
System.out.println("B: " + e.getMessage());
}
try {
Objects.requireNonNull(name, () -> "name は必須です(id=" + 42 + ")");
} catch (NullPointerException e) {
System.out.println("C: " + e.getMessage());
}
}
}
実行結果:
A: null
B: name は必須です
C: name は必須です(id=42)
| 形 | メッセージ | 使いどころ |
|---|---|---|
requireNonNull(x) |
なし(getMessage() は null) |
手早く書きたいとき |
requireNonNull(x, "説明") |
指定した文字列 | 基本はこれ。どの値が null だったかが残る |
requireNonNull(x, () -> "説明" + 値) |
Supplier が返す文字列 | メッセージに文字列連結や計算が入るとき |
メッセージなしの形は、例外のメッセージが空になります。 後からログを読む人のために、どの引数が null だったのかを書いた2つ目の形を基本にすると、原因にすぐたどり着けます。3つ目の Supplier を使う形は、null のときにだけメッセージを組み立てるので、正常なときに余分な文字列連結が起きません。
コンストラクタの入口で使う
requireNonNull は値をそのまま返すので、代入と同時に書けます。
import java.util.List;
import java.util.Objects;
public class Ctor {
record Member(String name, List<String> tags) {
Member {
Objects.requireNonNull(name, "name は必須です");
tags = (tags == null) ? List.of() : List.copyOf(tags);
}
}
public static void main(String[] args) {
System.out.println(new Member("sato", null));
new Member(null, null);
}
}
実行結果:
Member[name=sato, tags=[]]
Exception in thread "main" java.lang.NullPointerException: name は必須です
at java.base/java.util.Objects.requireNonNull(Objects.java:259)
at Ctor$Member.<init>(Ctor.java:8)
at Ctor.main(Ctor.java:15)
スタックトレースの2行目に、どのコンストラクタで止まったかが出ています。null のまま保存してしまうと、例外が出るのは「あとでその値を使った場所」になり、原因の場所から離れてしまいます。入口で確かめておくと、原因と例外の場所がそろいます。
通常のクラスでは、次のように書きます。
public class Task {
private final String title;
public Task(String title) {
this.title = Objects.requireNonNull(title, "title は必須です");
}
}
自分で if 文を書く場合との比較
| 書き方 | 行数 | 例外の型 | 向いている場面 |
|---|---|---|---|
Objects.requireNonNull(x, "説明") |
1行 | NullPointerException | null が「渡す側の誤り」である引数 |
if (x == null) throw new IllegalArgumentException("説明"); |
1〜3行 | IllegalArgumentException | null 以外の条件(範囲や形式)と同じ型でそろえたいとき |
どちらも「早めに知らせる」という目的は同じです。標準ライブラリは null の引数に対して NullPointerException を投げる方針なので、requireNonNull を使うとそれにそろいます。
requireNonNullElse で null のときの既定値を決める
null なら代わりの値を使いたいときは、Java 9 で加わった Objects.requireNonNullElse が1行で書けます。
import java.util.Objects;
import java.util.Optional;
public class DefaultDemo {
public static void main(String[] args) {
String name = null;
String city = "Tokyo";
System.out.println(Objects.requireNonNullElse(name, "名無し"));
System.out.println(Objects.requireNonNullElseGet(name, () -> "生成した既定値"));
System.out.println(Objects.toString(name, "未設定"));
System.out.println(Optional.ofNullable(name).orElse("名無し"));
System.out.println(Optional.ofNullable(city).map(String::toUpperCase).orElse("未設定"));
System.out.println(name != null ? name : "名無し");
}
}
実行結果:
名無し
生成した既定値
未設定
名無し
TOKYO
名無し
| 書き方 | 既定値も null のとき | 向いている場面 |
|---|---|---|
Objects.requireNonNullElse(x, 既定値) |
NullPointerException(メッセージは defaultObj) |
既定値が決まっていて、結果が必ず null でないことを保証したい |
Objects.requireNonNullElseGet(x, supplier) |
NullPointerException | 既定値を作るのに計算や生成が必要 |
x != null ? x : 既定値 |
null がそのまま返る | Java 8 以前でも使いたい |
Optional.ofNullable(x).orElse(既定値) |
null がそのまま返る | 途中に map や filter をはさみたい |
Objects.toString(x, "未設定") |
null がそのまま返る | 表示用の文字列にしたい |
requireNonNullElse の特徴は、戻り値が必ず null でないことです。既定値にも null を渡すと、次のように知らせてくれます。
try {
Objects.requireNonNullElse(name, null);
} catch (NullPointerException e) {
System.out.println("D: " + e.getMessage());
}
D: defaultObj
requireNonNullElse と requireNonNullElseGet の選び方
requireNonNullElse(x, createDefault()) と書くと、x が null でなくても createDefault() は先に実行されます。Java では、メソッドを呼ぶ前に引数がすべて評価されるためです。既定値を作る処理に時間がかかるとき、あるいは呼ぶたびに何かが起きるときは、requireNonNullElseGet(x, () -> createDefault()) を選ぶと、null のときだけ実行されます。固定の文字列や定数を渡すだけなら requireNonNullElse で十分です。
Optional は戻り値で使う
Optional は、「値がないかもしれない」ことを型で伝えるための入れ物です。主な使いどころはメソッドの戻り値で、呼び出した側に「ない場合の扱い」を決めてもらえます。
import java.util.Map;
import java.util.Optional;
public class OptionalDemo {
static final Map<Integer, String> NAMES = Map.of(1, "sato", 2, "suzuki");
// 見つからないかもしれない、を戻り値の型で伝える
static Optional<String> findName(int id) {
return Optional.ofNullable(NAMES.get(id));
}
public static void main(String[] args) {
System.out.println(findName(1).orElse("該当なし"));
System.out.println(findName(9).orElse("該当なし"));
System.out.println(findName(1).map(String::toUpperCase).orElse("該当なし"));
findName(2).ifPresent(n -> System.out.println("見つかりました: " + n));
}
}
実行結果:
sato
該当なし
SATO
見つかりました: suzuki
findName(9) のように見つからない場合でも例外にはならず、orElse に渡した値が使われています。
Optional の生成で気をつけたい点を、実行した結果で示します。
try { Optional.of(name); } // name は null
catch (NullPointerException e) { System.out.println("E: " + e.getMessage()); }
try { Optional.ofNullable(name).orElseThrow(); }
catch (Exception e) { System.out.println("F: " + e); }
E: null
F: java.util.NoSuchElementException: No value present
| メソッド | null を渡したとき |
|---|---|
Optional.of(x) |
NullPointerException(null でないとわかっている値に使う) |
Optional.ofNullable(x) |
空の Optional になる |
Optional.empty() |
空の Optional を作る |
Optional と、ここまでに紹介した書き方の使い分けは次のとおりです。
| 場面 | 合う書き方 |
|---|---|
| メソッドの戻り値で「見つからない」を表したい | Optional<T> |
| 引数が null でないことを確かめたい | Objects.requireNonNull |
| フィールドの値がないことを表したい | null を許すか、空のコレクション・空文字などの既定値を持たせる |
| ローカル変数の null を一度だけ判定したい | if (x == null) |
orElse と orElseGet の違いなど、Optional の各メソッドの詳しい使い方は、それだけで1つの主題になるため、ここでは使いどころの整理にとどめます。
Java 14以降の詳細なNullPointerExceptionメッセージ
nullチェックが抜けていた場所で例外が起きたとき、現在の Java は「何が null だったか」をメッセージで教えてくれます。 この機能は Java 14 で加わり、Java 15 からは最初から有効になっている、として一般に知られた仕様です(JEP の原文は今回開いていません)。ここでは Java 21 で実際の出力を確認します。
public class Npe {
record Address(String city) { }
record Member(String name, Address address) { }
public static void main(String[] args) {
Member m = new Member("sato", null);
int[] arr = null;
try { System.out.println(m.address().city().length()); }
catch (NullPointerException e) { System.out.println("1: " + e.getMessage()); }
try { Member none = null; System.out.println(none.name()); }
catch (NullPointerException e) { System.out.println("2: " + e.getMessage()); }
try { System.out.println(arr[0]); }
catch (NullPointerException e) { System.out.println("3: " + e.getMessage()); }
try { System.out.println(arr.length); }
catch (NullPointerException e) { System.out.println("4: " + e.getMessage()); }
System.out.println(m.address().city().length());
}
}
javac -g Npe.java でコンパイルして java Npe を実行した結果:
1: Cannot invoke "Npe$Address.city()" because the return value of "Npe$Member.address()" is null
2: Cannot invoke "Npe$Member.name()" because "none" is null
3: Cannot load from int array because "arr" is null
4: Cannot read the array length because "arr" is null
Exception in thread "main" java.lang.NullPointerException: Cannot invoke "Npe$Address.city()" because the return value of "Npe$Member.address()" is null
1つ目のメッセージに注目してください。m.address().city().length() のようにメソッド呼び出しが連なった行でも、「address() の戻り値が null だったので city() を呼べなかった」と、どの部分が原因かまで特定できます。行番号だけではわからなかった情報です。
メッセージの読み方
| メッセージの形 | 意味 |
|---|---|
Cannot invoke "A.b()" because "x" is null |
変数 x が null なのに x.b() を呼んだ |
Cannot invoke "A.b()" because the return value of "C.d()" is null |
d() の戻り値が null なのに、続けて b() を呼んだ |
Cannot load from int array because "arr" is null |
配列 arr が null なのに arr[0] を読んだ |
Cannot read the array length because "arr" is null |
配列 arr が null なのに arr.length を読んだ |
Cannot invoke "java.lang.Integer.intValue()" because ... is null |
null の Integer を int に変換しようとした |
Cannot invoke "String.hashCode()" because ... is null |
null の文字列を switch に渡した |
最後の2つも今回の実行で確認しました。Integer n = null; int i = n; と、null の String を switch (name) に渡したときの出力です(-g なしで実行)。
I: Cannot invoke "java.lang.Integer.intValue()" because "<local4>" is null
J: Cannot invoke "String.hashCode()" because "<local4>" is null
変数名が <local4> と表示されるとき
上の出力では、変数名のところが <local4> になっています。これは、コンパイル時にローカル変数の名前がクラスファイルへ記録されていないときの表示です。同じ Npe.java を、オプションを変えて動かした結果を比べます。
| 動かし方 | 2つ目のメッセージ |
|---|---|
javac -g Npe.java → java Npe |
Cannot invoke "Npe$Member.name()" because "none" is null |
javac Npe.java(-g なし)→ java Npe |
Cannot invoke "Npe$Member.name()" because "<local3>" is null |
java Npe.java(単一ファイル実行) |
Cannot invoke "Npe$Member.name()" because "<local3>" is null |
java -XX:-ShowCodeDetailsInExceptionMessages Npe |
null(メッセージなし) |
ローカル変数名まで表示したいときは、javac -g でコンパイルします。 フィールド名・メソッド名・配列の操作といった情報は -g がなくても表示されます。ビルドツールを使っている場合にこのオプションがどう設定されるかは、今回は確かめていません(未実行)。
最後の行の -XX:-ShowCodeDetailsInExceptionMessages は、この詳細メッセージを無効にするJVMオプションです。無効にすると getMessage() は null になり、スタックトレースは Exception in thread "main" java.lang.NullPointerException と行番号だけになりました。
requireNonNull のメッセージとの関係
詳細メッセージがあっても、requireNonNull にメッセージを書く意味は変わりません。
| 比べる点 | 詳細な NullPointerException メッセージ | requireNonNull(x, "説明") |
|---|---|---|
| 例外が起きる場所 | null を実際に使った場所 | 入口で確かめた場所 |
| メッセージの内容 | コード上の式(メソッド名・変数名) | 自分で書いた説明(何が必須か) |
| 書く手間 | なし | 1行 |
「入口で requireNonNull、抜けていた場所は詳細メッセージで追う」という二段構えにすると、null が原因の例外を短い時間で調べられます。
文字列の null・空文字・空白を判定するパターン
文字列では、null のほかに「空文字("")」と「空白だけ(" ")」も「値が入っていない」として扱いたい場面がよくあります。標準ライブラリだけで書ける判定を、コピーして使える形でまとめます。
public class StrCheck {
// null または空文字
static boolean isNullOrEmpty(String s) {
return s == null || s.isEmpty();
}
// null・空文字・空白だけ(Java 11 以降)
static boolean isNullOrBlank(String s) {
return s == null || s.isBlank();
}
// 何か文字が入っている(Java 11 以降)
static boolean hasText(String s) {
return s != null && !s.isBlank();
}
public static void main(String[] args) {
String[] samples = { null, "", " ", " ", "\t\n", "abc", " abc " };
for (String s : samples) {
System.out.println(s + " | " + isNullOrEmpty(s) + " | "
+ isNullOrBlank(s) + " | " + hasText(s));
}
}
}
7種類の入力で、各判定と trim()・strip() の結果を動かして確かめた表です(" " は全角スペース1文字)。
| 入力 | == null |
isNullOrEmpty | isNullOrBlank | hasText | trim().isEmpty() |
strip().isEmpty() |
|---|---|---|---|---|---|---|
| null | true | true | true | false | (例外になるため未評価) | (同左) |
"" |
false | true | true | false | true | true |
" "(半角スペース) |
false | false | true | false | true | true |
" "(全角スペース) |
false | false | true | false | false | true |
"\t\n"(タブと改行) |
false | false | true | false | true | true |
"abc" |
false | false | false | true | false | false |
" abc " |
false | false | false | true | false | false |
全角スペースだけの文字列は、trim().isEmpty() では false、isBlank() と strip().isEmpty() では true になりました。trim() が取り除くのは半角スペースやタブなどの一部の文字で、全角スペースは対象に含まれないためです。日本語の入力を扱うときは、Java 11 以降の isBlank() か strip() を使うと、見た目どおりの判定になります。
やりたいこと別のコピペ用パターン
| やりたいこと | 書き方 | 対応バージョン |
|---|---|---|
| null か空文字かを調べる | s == null || s.isEmpty() |
Java 6 以降 |
| null・空文字・空白だけを調べる | s == null || s.isBlank() |
Java 11 以降 |
| 上と同じことを Java 8 で書く | s == null || s.trim().isEmpty()(全角スペースは対象外) |
Java 8 以前でも可 |
| 文字が入っていることを調べる | s != null && !s.isBlank() |
Java 11 以降 |
| null を空文字に置き換える | Objects.requireNonNullElse(s, "") |
Java 9 以降 |
| null を空文字に置き換える(Java 8) | Objects.toString(s, "") |
Java 7 以降 |
| 空白だけなら既定値を使う | Optional.ofNullable(s).map(String::strip).filter(x -> !x.isEmpty()).orElse("既定値") |
Java 11 以降 |
| null かもしれない文字列と定数を比べる | "abc".equals(s) または Objects.equals(s, "abc") |
Java 7 以降 |
「空白だけなら既定値を使う」のパターンに " "(半角スペース2つ)を渡したところ、既定値 と表示されました。Objects.requireNonNullElse(null, "").isEmpty() は true でした。
null かもしれない値を比べる・表示する
String name = null;
System.out.println(Objects.equals(name, "x")); // false
System.out.println(Objects.equals(null, null)); // true
System.out.println("x".equals(name)); // false
System.out.println(Objects.hashCode(name)); // 0
System.out.println(String.valueOf((Object) name)); // null という文字列
System.out.println("値は " + name); // 値は null
実行結果:
false
true
false
0
null
値は null
比較では、定数を左側に置く "x".equals(name) か、Objects.equals(name, "x") を使うと、name が null でも例外になりません。 name.equals("x") と書いた場合は、次のメッセージで例外になりました。
H: Cannot invoke "String.equals(Object)" because "<local1>" is null
また、文字列連結では null が "null" という4文字になります。画面やファイルにそのまま出したくないときは、Objects.toString(name, "未設定") で表示用の文字列を決めておきます。
null に出会いやすい場所と備え方
| 場所 | 起きること | 備え方 |
|---|---|---|
Map.get(key) |
キーがないと null が返る | getOrDefault(key, 既定値) を使う、または戻り値を確かめる |
| ラッパー型から基本型への変換 | Integer n = null; int i = n; で例外 |
変換の前に null を確かめる、requireNonNullElse(n, 0) |
String や enum の switch |
値が null だと例外 | 手前で null を確かめる、Java 21 以降は case null を書く |
List.of / Map.of |
要素に null を入れると例外 | 入れる前に filter(Objects::nonNull) で除く |
| メソッドが返すコレクション | null を返すと呼び出し側の判定が増える | 空のコレクション(List.of())を返すと決める |
| 連なったメソッド呼び出し | 途中のどれかが null だと例外 | 詳細メッセージで原因を特定し、入口で確かめる |
List.of("a", null) は、実際に NullPointerException になりました(メッセージは null)。また、Java 21 では switch に case null を書けます。
Object o = null;
String r = switch (o) {
case null -> "nullです";
case String s -> "文字列 " + s;
default -> "その他";
};
System.out.println(r);
実行結果は nullです でした。case null を書かない switch に null を渡すと、これまでどおり NullPointerException になります。
nullチェックを減らす設計のチェックリスト
| 確認すること | ねらい |
|---|---|
| コレクションを返すメソッドは、null ではなく空のコレクションを返している | 呼び出し側の nullチェックが不要になる |
コンストラクタや public メソッドの入口で requireNonNull している |
null がオブジェクトの中に入り込まない |
「見つからないことがある」メソッドの戻り値は Optional にしている |
呼び出し側が扱いを決め忘れない |
フィールドを final にし、宣言時かコンストラクタで必ず値を入れている |
未設定のまま使われることがなくなる |
文字列の比較は "定数".equals(x) か Objects.equals にそろえている |
比較の行で例外にならない |
表示用の文字列は Objects.toString(x, 既定の文字列) を通している |
画面に null と出ない |
よくある質問
Q. x == null と Objects.isNull(x) はどちらを使えばよいですか?
A. 結果は同じです。if 文では x == null、Stream の filter や Predicate を渡す場面では Objects::isNull・Objects::nonNull と使い分けると読みやすくなります。Objects.isNull と nonNull は Java 8 で加わったメソッドです。
Q. Objects.requireNonNull はどこに書くのがよいですか?
A. コンストラクタや public メソッドの最初です。値を受け取った時点で確かめておくと、例外のスタックトレースが「null を渡した呼び出し」を指すようになります。第2引数に "name は必須です" のような説明を付けると、今回の出力のようにメッセージへそのまま表示されます。
Q. requireNonNullElse と Optional.ofNullable(x).orElse(...) の違いは何ですか?
A. null のときに既定値を返す点は同じです。requireNonNullElse は既定値も null だと例外を投げ、戻り値が null でないことを保証します。 orElse は既定値が null ならそのまま null を返します。途中で map や filter を使いたいなら Optional、置き換えるだけなら requireNonNullElse が短く書けます。requireNonNullElse は Java 9 以降で使えます。
Q. NullPointerException のメッセージに <local4> と出ます。変数名にできますか?
A. javac -g でコンパイルすると、ローカル変数の名前がクラスファイルに記録され、"none" is null のように変数名で表示されます。今回の実行では、-g なしと単一ファイル実行(java Npe.java)のどちらも <local3> の表示でした。
まとめ
- 判定するだけなら
x == null。Objects.isNull・nonNullは同じ結果で、filter(Objects::nonNull)のようなメソッド参照に向いている。 - null を受け付けない引数は、入口で
Objects.requireNonNull(x, "説明")。 メッセージなしの形はgetMessage()が null になるので、説明を付ける。 - null のときの既定値は
Objects.requireNonNullElse(x, 既定値)(Java 9 以降)。 既定値を作る処理が重いときはrequireNonNullElseGet。 Optionalは戻り値で「値がないかもしれない」を伝えるために使う。Optional.of(null)は例外、ofNullable(null)は空になる。- Java 21 では、NullPointerException のメッセージに「何が null だったか」が出る。 ローカル変数名まで出すには
javac -g。 - 文字列は
s == null || s.isBlank()で null・空文字・空白をまとめて判定できる(Java 11 以降)。 全角スペースはtrim()では取り除かれない。 - 空のコレクションを返す・入口で確かめる・比較は
Objects.equals、の3つでnullチェックそのものを減らせる。
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「Java nullチェックの書き方|requireNonNullとisNull」
株式会社RJCでは、Javaのnullチェックのような基本の書き方を仲間と学び合っています。こうした知識を共有しながら、日々技術を磨いています。
株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。
WE ARE HIRING
RJCで一緒に開発しながら、
成長を楽しみませんか?
RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!
- 127日年間休日
- 12時間平均残業時間
- 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!
ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。