LoginSignup
8
3

More than 1 year has passed since last update.

未だにNPEを出すエンジニアが反省する備忘録

Last updated at Posted at 2022-11-04

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の墓標です。さようなら。

8
3
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
3