チームで取り組む！JavaでNullと真正面から向き合う

> 2009年のカンファレンスでNull参照を発明したことについて謝罪している。
>
> それは10億ドルにも相当する私の誤りだ。

Null安全でない言語を使っている開発者がいつも悩まされている憎き問題です。
計算機科学者である [アントニー・ホーア | wikipedia](https://ja.wikipedia.org/wiki/%E3%82%A2%E3%83%B3%E3%83%88%E3%83%8B%E3%83%BC%E3%83%BB%E3%83%9B%E3%83%BC%E3%82%A2) が上記のような発言をしたとおり、言語に織り込まれた技術的負債といっても過言ではありません。

現代の新規開発においては [null安全でない言語は、もはやレガシー言語だ | Qiita](https://qiita.com/koher/items/e4835bd429b88809ab33) 記事のようにNull安全でない言語自体を回避するという考え方もありでしょう。
しかしながら、様々な理由でNull安全でない言語を選択しなければいけない場面は尚存在しています。

そこで今回はJavaにおけるNullと向き合う際の手法について考えていきます。

<img width="400" alt="スクリーンショット 2018-06-20 19.31.07.png" src="https://qiita-image-store.s3.amazonaws.com/0/93055/3ec908ce-bd96-2cf0-8ea6-8438fdf529e9.png">

## 読み進める上での注意点
- 対策案には「オススメ度」をつけていますが経験に基づく個人的な感想です
- チームで取り入れる際のおすすめ度は3段階で示しています（ :star: 〜 :star: :star: :star: ）

## 策

### [JSR-305](https://jcp.org/en/jsr/detail?id=305)
Javaの仕様として[ソフトウェア欠陥検出](https://www.infoq.com/news/2008/06/jsr-305-update)のために定義されたアノテーション群です。

そのため、Runtimeで動作を期待されたアノテーションではなく、あくまで開発時の**補助的な機能**として定義された仕様です（たとえば、Find BugsやIntelliJなどのIDEでのサポート）。

#### 戻り型が `null` かどうかを宣言する
オススメ度： :star: :star: :star: 

```java
import javax.annotation.Nullable;

@Nullable
public String createText() {
    // ...
```
```java
import javax.annotation.Nonnull;

@Nonnull
public String createText() {
    // ...
```
チェックサポートの機能を利用できることもとても重要ですが、チームとして取り組む場合は以下点で効力が最大化されると感じました。

- 設計・実装者のメソッドごとの意図が明確に残せる
    - 副作用としてレビュー時の質が向上する
- メソッドを利用する実装者がnullについて考慮する手間が減る
    - 不要なnullチェックが削減される

#### 引数に `null` を許容するかどうかを宣言する
オススメ度： :star: 

```java
import javax.annotation.Nullable;
import javax.annotation.Nonnull;

public String createText(@Nonnull String id,
                         @Nullable String option) {
    // ...
```
「戻り型が `null` かどうかを宣言」と同様のメリットを得られる一方で、チームとしては以下デメリットが目立ちました。

- すべての引数について付加するのは手間である
    - 手間であるがゆえに形骸化しやすい
    - 引数がアノテーション祭り状態となり、視認性が落ちる

### Java 8+ 標準 `Optional`
#### `Optional` で戻り型として `null` を示唆する
オススメ度： :star: 

```java
import java.util.Optional;

public Optional<String> getText() {
   // ...
```
Java 8 `Optional` については数多の記事で言及があるので詳細は省きます。
ここであまりオススメでない理由としては以下が挙げられます。

- `Optional` 型はシリアライズ可能(Serializable)なクラスではないため、使い方が限定される
    - ライブラリ（特にO/R Mapper系）がサポートされていないケースではルールが破綻しやすい

■ 参考

- [Java 8 "Optional" ～ これからのnullとの付き合い方 ～](https://qiita.com/shindooo/items/815d651a72f568112910)
- [OptionalがSerializableではない話と使い方まとめ](http://d.hatena.ne.jp/nowokay/20150901)


### 表明プログラミング
[達人プログラマー](https://www.amazon.co.jp/dp/B06W567M44/)で紹介されている事前条件をチェックする「表明プログラミング」。
設計/実装者の「より高いレベルのコンセプト」をコードとして残すことができるため、非常に価値があります。

#### プログラムの前提を `assert` でテストする
オススメ度： :star: :star: 

```java
private String createText(String id) {
    assert (id != null) : "id must not be null.";
    // ...
```

Javaでの表明プログラミングの代表例といえば `assert` でしょう。
ただし、コードとしては以下点に注意する必要があります（[アサーションを使用したプログラミング | oracle.com](https://docs.oracle.com/javase/jp/8/docs/technotes/guides/language/assert.html) からの抜粋）

> ■ アサーションの有効化および無効化
**デフォルトでは、実行時にアサーションは無効**になっています。2つのコマンド行スイッチを使用して、アサーションの有効/無効を切り替えることができます。
> 様々な詳細レベルでアサーションを有効にするには、-enableassertionsまたは-eaスイッチを使用します。

`assert` はあくまで実装者の意思表明のための補助手段であり、プロダクトコードのRuntimeで例外を発生させる用途ではない、と読み取ることができます。
また公式ドキュメントを注意深く読むと、副作用のある処理についてなど重要な記載されていますので採用の際にはどういった点で使用するか熟考する必要があります。

#### ライブラリのアサーションを活用する
オススメ度： :star: :star: :star: 

```java
import org.springframework.util.Assert;

public String createText(String id) {
    Assert.notNull(id, "ID must not be null.");
    // ...
```
Springフレームワークの場合 [`org.springframework.util.Assert`](https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/util/Assert.html) の利用がそれにあたります。

JavaのAssertionとは違い、Runtimeであっても例外を発生させます。
nullチェックの簡略化ではありますがRuntimeでも動作するため、実装者の非常に強い意思表明をコードに残すことができます。

### 間接的な `null` 防御

#### 変数を `final` 宣言しImmutableにする
オススメ度： :star: :star:

```java
public String createText(final String id) {
    // ...
```
```java
public String createText(String id) {
    final String prefix = something(id);
    // ...
```
`final`の使用は`null`とは直接的な関わりはなさそうに見えます。
しかしAssertionでの非`null`保証との組み合わせで、自メソッド内で非 `null` をほぼ完全保証できます。
結果、中間処理での「何が来るかわからないのでとりあえず`null`チェック入れる」ような不要な処理を自信を持って削除できます。

#### CollectionをImmutableにする
オススメ度： :star: :star: 

##### Java標準
```java
import java.util.Collections;

public String createText() {
    List immutables = Collections.singletonList("value1", "value2");
    // ...
```
```java
import java.util.Collections;

public String createText() {
    Map<String, String> immutables = Collections.singletonMap("key1", "value1");
    // ...
```
```java
import java.util.Collections;

public List<Store> fetchStores() {
   List<Store> stores  = findAll();
   
   return Collections.unmodifiableCollection(stores);
```
##### [Guava](https://github.com/google/guava)
```java
import com.google.common.collect.ImmutableMap;

public String createText() {
    Map<String, String> immutables = ImmutableMap.of("key1", "value1", "key2", "value2");
    // ...
```
```java
import com.google.common.collect.ImmutableList;

public String createText() {
    List<String> immutables = ImmutableList.of("value1", "value2");
    // ...
```
コレクションのImmutable化に関してはかなり局所的な防御となります。

後続の中間処理での`null`例外をケアすることを目的として、一度作成したコレクションに`null`要素を混ぜ込まれる危険を減らします。
地味ではありますが可能な限り取り入れるべきです。

## 用例
では実際に適用前/後でどのくらい情報量に差が出るかを見てみましょう。

### before
```java
public class StoreService {
    
    public Store fetchStore(String storeId){
        // ...
    }

    public List<Store> fetchSisterStores(String storeId){
        // ...
    }

    public List<Coupon> fetchCoupons(String storeId,
                                         List<Category> categories){
        // ...
    }
}
```

### after
```java
public class StoreService {

    @Nonnull
    public Store fetchStore(final String storeId) {
        Assert.notNull(storeId, "store-id must not be null.");
        // ...

    }

    @Nullable
    public List<Store> fetchSisterStores(final String storeId) {
        Assert.notNull(storeId, "store-id must not be null.");

        // ...
    }

    @Nonnull
    public List<Coupon> fetchCoupons(final String storeId,
                                     final List<Category> categories) {
        Assert.notNull(storeId,    "store-id must not be null.");
        Assert.notNull(categories, "categories must not be null.");

        // ...
    }
}
```
より表現に富んだコードになったのではないでしょうか？
シグニチャの前後の数行を読むだけで、大体の用例を即時に吸い上げることができ利用者にも優しいコードとなりました。

レビュアーの視点で見てみると `fetchSisterStores( ... )` と `fetchCoupons( ... )` で返す `List` 型の仕様の異なりがより鮮明に浮き上がり、レビューの質も上がりそうですよね。

## まとめ
チームとしての`null`対策を選択し、`null`による技術的負債を極限まで削ぎ落としましょう！
そしてチーム全員で以下のメリットを実感していきましょう！

- コードの可読性の向上
- レビュー品質の向上
- ヌルポで枕を濡らす夜の減少

それではよき `null` ライフを！