JavaにおけるNPE(Null Pointer Exception)は「nullのオブジェクトを使おうとしてるぞ」という例外です。例えばmyNote.getLines()
という文はコンパイル時にエラーを出しません。ただ、myNote==null
の時にnull.getLines()
を実行することになり「nullにgetLines()なんてメソッドはない」という原因でNPEが投げられます。
Javaの参照型オブジェクトは全てnullが入りうるのですが、それを利用すると例外が出ます。危ない。「null安全」という言葉があるくらいですから、丁重に扱うべきでしょう。
nullを安全に扱う方法や仕組みは色々ありますが主にOptionalを使った方法をメモします。(本記事はJava SE 8を念頭に置いて書かれています。)
やってしまいがちなミス
「いたる所にnullが入りうるからnullチェックをしよう」で終わる話ではありますが、ミスを並べます。
if文の中
長く書きたくないので試行錯誤してたらnullチェックを忘れたりしてしまうかもしれない。
nullチェックの順序が逆
// 出る
if(myNote.getLines().size() > 0 && myNote.getLines() != null)
// 出ん
if(myNote.getLines() != null && myNote.getLines().size() > 0)
Objects.equals()を使った方がいいパターン
// 出る
if(myNote.getTitle().equals("No title"))
// 出ん
if("No title".equals(myNote.getTitle()))
// 安全
if(Objects.equals(myNote.getTitle(), "No title"))
深くネストしたオブジェクトの中
うっかりしがち。
// getTitle()がnullだと結局出る
if(Objects.equals(myNote.getTitle().length(), 5))
// 出ない(もっと良い書き方があるかもしれない)
if(Objects.nonNull(myNote.getTitle())
&& Objects.equals(myNote.getTitle().length(), 5))
多分最初にnullチェックした方が良いケース
// とても入り組んだ複雑な文
complex.getAList().stream().map(a -> a.getBList().stream()...)
// 例えばこんな感じ…?
if(Objects.isNull(complex.getAList())){
return null;
}
complex.getAList().stream().filter(Objects::nonNull)...
それっぽいスニペット
Optionalに関連した例
【過去にコンパイルエラーの出る例を出していました(ラムダ式内での再代入) 2022/11/07 14:30修正】
// barがnullだと、NPEが出る
List<String> baz = foo.getBar()...【barに関する処理】
// barがnullだと、bazには空のArrayListが入る
List<String> baz = Optional.ofNullable(foo.getBar())
.map(e -> 【barをList<String>に変換するような処理】)
.orElse(new ArrayList<String>());
// barがnullだと、何も処理せずに進む
Optional.ofNullable(item.getVariants()).ifPresent(e -> {
【barを利用する処理】
});
Collectionのサイズを出す例、
Collection内の要素を操作する入れ子の例
// List<String> lines はノートに書かれた1行1行をListとして保持する
// 行数を出す
Integer sizeOfCollection = Optional.ofNullable(myNote.getLines())
.map(lines -> lines.size())
.orElse(0);
// 行毎の文字数を出す (インデントは大袈裟にしています)
List<Integer> sizeOfCollection = Optional.ofNullable(myNote.getLines())
.map(lines -> lines.stream()
.map(line -> line.length())
.collect(Collectors.toList())
)
.orElse(new ArrayList<>());
ディープコピー(参考)
List<String> b = Optional.ofNullable(a)
.map(list -> (List<String>) new ArrayList<>(list))
.orElseGet(Collections::emptyList);
最後に
「面倒くさい。やりたいことが代入だけなのに3行も使わなきゃいけないの?だいたい、Javaの言語設計が悪いんじゃないのか?10億ドルの失敗は円安で加速する…!」とか言わずに丁寧なコーディングを積み重ねる。これが良いエンジニアになる一歩だと身に染みました。2022年にもなって出していい例外ではないので反省文を書きました。ここがNPEの墓標です。さようなら。