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

Javaにおける[NPE(Null Pointer Exception)](https://docs.oracle.com/javase/jp/8/docs/api/java/lang/NullPointerException.html)は「nullのオブジェクトを使おうとしてるぞ」という例外です。例えば`myNote.getLines()`という文はコンパイル時にエラーを出しません。ただ、`myNote==null`の時に`null.getLines()`を実行することになり「nullにgetLines()なんてメソッドはない」という原因でNPEが投げられます。

Javaの参照型オブジェクトは全てnullが入りうるのですが、それを利用すると例外が出ます。危ない。「[null安全](https://atmarkit.itmedia.co.jp/ait/articles/1803/15/news012.html)」という言葉があるくらいですから、丁重に扱うべきでしょう。

nullを安全に扱う方法や仕組みは色々ありますが主に[Optional](https://docs.oracle.com/javase/jp/8/docs/api/java/util/Optional.html)を使った方法をメモします。(本記事はJava SE 8を念頭に置いて書かれています。)

# やってしまいがちなミス
「いたる所にnullが入りうるからnullチェックをしよう」で終わる話ではありますが、ミスを並べます。

## if文の中
長く書きたくないので試行錯誤してたらnullチェックを忘れたりしてしまうかもしれない。

nullチェックの順序が逆
```java
// 出る
if(myNote.getLines().size() > 0 && myNote.getLines() != null)
// 出ん
if(myNote.getLines() != null && myNote.getLines().size() > 0)
```
[Objects.equals()](https://docs.oracle.com/javase/jp/8/docs/api/java/util/Objects.html#equals-java.lang.Object-java.lang.Object-)を使った方がいいパターン
```java
// 出る
if(myNote.getTitle().equals("No title"))
// 出ん
if("No title".equals(myNote.getTitle()))
// 安全
if(Objects.equals(myNote.getTitle(), "No title"))
```

## 深くネストしたオブジェクトの中
うっかりしがち。
```java
// getTitle()がnullだと結局出る
if(Objects.equals(myNote.getTitle().length(), 5))
// 出ない（もっと良い書き方があるかもしれない）
if(Objects.nonNull(myNote.getTitle()) 
    && Objects.equals(myNote.getTitle().length(), 5))
```

## 多分最初にnullチェックした方が良いケース
```java
// とても入り組んだ複雑な文
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修正】
```java
// 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内の要素を操作する入れ子の例
```java
// 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<>());
```

ディープコピー（[参考](https://stackoverflow.com/questions/689370/how-to-copy-java-collections-list)）
```java
List<String> b = Optional.ofNullable(a)
    .map(list -> (List<String>) new ArrayList<>(list))
    .orElseGet(Collections::emptyList);
```

# 最後に
「面倒くさい。やりたいことが代入だけなのに3行も使わなきゃいけないの？だいたい、Javaの言語設計が悪いんじゃないのか？[10億ドルの失敗](https://doc.rust-jp.rs/book/second-edition/ch06-01-defining-an-enum.html#option-enum%E3%81%A8null%E5%80%A4%E3%81%AB%E5%8B%9D%E3%82%8B%E5%88%A9%E7%82%B9)は円安で加速する…！」とか言わずに丁寧なコーディングを積み重ねる。これが良いエンジニアになる一歩だと身に染みました。2022年にもなって出していい例外ではないので反省文を書きました。ここがNPEの墓標です。さようなら。