`get()`を使うな ～ 敗北者の Optional

Java の Optional に関する記事を見ると “この教え方では **Optional の意味がまるっきりない使い方するやつ**ばかりが生まれるぞ……” というのを数多く見てしまったので,せめてもの反抗にこの記事を書きます。

## TL; DR

`isPresent()`と`get()`しか知らない奴は

![yamero.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/241027/bdc325bf-4dd7-e91d-b11e-bc21f5b6281e.png)

## こういう書き方をしてはいけない

記事を見ていると

> null チェックは`Optional#isPresent()`で行い,値は`Optional#get()`で取り出します。

みたいな解説が多すぎる。

**そういうことをしてはいけない**。それでは**従来の null チェックとなにも変わらない**。変わらないどころかタイプ数が増え,実行時間が増え,メモリ使用量が増え**,デメリットだけが上乗せされている**。メリットはなにも得られていないのに。

```java
Optional<String> someStr = Optional.of("Hello, world!");

if (someStr.isPresent()) {
    System.out.println(someStr.get()); // やってはいけない
}
```

まず頭に叩き込んでほしいのは**`Optional#get()`を使ってはいけない**ということだ。

大事なことなのでもう一度言う。

<span style="font-size: 200%;">**`Optional#get()`は使うな**。</span>

`Optional#get()`は**それが null かどうかを気にせずに取り出すウンコみたいなメソッド**だ。**Unsafe** なのだ。“どーしてもそれを使わないといけないとき” のため**だけ**に用意されている。

<!-- “直前に null チェックしてるからいいじゃん” とか抜かすアホは **Antony Hoare にケツをファックされてからこの記事に戻ってくる**ように。“null チェックしてるからいい” なら**従来の方法で十分**なのだ。**無意味**だ。お前が **null チェックを忘れようが**`Optional#get()`は使える。**そしてクラッシュする**。バカバカしい話だ。 -->

“直前に null チェックしてるからいいじゃん” とか抜かす奴は **Antony Hoare にぶち怒られてからこの記事に戻ってくる**ように。“null チェックしてるからいい” なら**従来の方法で十分**なのだ。**無意味**だ。お前が **null チェックを忘れようが**`Optional#get()`は使える。**そしてクラッシュする**。バカバカしい話だ。

ではどうするか。**`Optional#ifPresent()`を使う**。このメソッドはメソッド参照を引数に取る。ので,ラムダ式を中に記述していくが……**ラムダ式もメソッド参照も知らなくてもそれっぽく書けばいい**。

```java
Optional<String> someStr = Optional.of("Hello, world!");

someStr.ifPresent(x -> {
    System.out.println(x);
});
```

`Optional#ifPresent()`に渡されたメソッド参照は **null でないときだけ実行される**。`x`は**一時的に使える変数**だと思っていい。もちろん名前は (かぶらない限りは) 自由だ。型は当然`Optional<T>`に対して`T`となる。ラムダ式やメソッド参照について知っておくのが一番だが,知らなくても**通常の if 文と見た目は似ている**ので十分のはずだ。

null でないときだけ実行したいのではなく,**値の有無で動作を分けたい**場合もあるかもしれない。

```java
Optional<String> someStr = Optional.empty();

if (someStr.isPresent()) {
    System.out.println(someStr.get()); // もちろんやってはいけない
} else {
    System.out.println("値はなかったよ");
}
```

そういうときは**`Optional#ifPresentOrElse()`を使用する**。

```java
Optional<String> someStr = Optional.empty();

someStr.ifPresentOrElse(x -> {
    System.out.println(x);
}, () -> {
    System.out.println("値はなかったよ");
});
```

`() -> ...`は引数を持たないラムダ式を意味するが,もちろん“値を使わないときのおまじない”だと思ってくれても構わない。

なお,`Optional#ifPresentOrElse()`は Java 9 で実装されたので,Java 8 の環境では

```java
Optional<String> someStr = Optional.empty();

someStr.ifPresent(x -> {
    System.out.println(x);
}); if (!someStr.isPresent()) {
    System.out.println("値はなかったよ");
}
```

とするしかないだろう。

## なるべく`Optional#ifPresent()`も使うな

**できれば`Optional#ifPresent()`も濫用すべきではない**。`Optional#map()`や`Optional#flatMap()`や`Optional#orElse()`で十分なときが多いはずだ。Optional の世界では **null チェックなんてしないのが基本**なのだ。

たとえば,先に挙げた`Optional#ifPresentOrElse()`の例などはこれらのメソッドを使えばより簡潔に書ける。`empty`だったときの代用値を指定してアンラップする`Optional#orElse()`を使用すれば,

```java
Optional<String> someStr = Optional.empty();

System.out.println(someStr.orElse("値はなかったよ"));
```

これでよい。もし`Optional<String>`ではなく`Optional<Integer>`だったら? ならば`empty`でないときだけ関数適用する`Optional#map()`の出番だ。

```java
Optional<Integer> someInt = Optional.empty();

System.out.println(someInt.map(x -> x.toString()).orElse("値はなかったよ"));
```

あなたがメソッド参照について勉強すればさらに簡潔に書ける。

```java
Optional<Integer> someInt = Optional.empty();

System.out.println(someInt.map(Object::toString).orElse("値はなかったよ"));
```

無論,一行が長いと思ったら外に変数を定義して引き出してもいい。

より多彩な例は[こちらの記事](https://qiita.com/koher/items/6f4a8d8b3ad3142bf645)などを参照。

## 敗北者が多すぎて敗北者向けの記事が書かれる始末

敗北者を生む自称解説記事が多いために`Optional#get()`はそもそも使わないとか,null チェック自体しないのが理想という基本の基本部分があまり知られていないので “[Optional などないほうがマシである](https://qiita.com/chooyan_eng/items/02f775f591c0312a9faf)” というような**トンチンカンな記事**が書かれてしまうことになる。

> * `NullPointerException`を`NoSuchElementException`にすり替えます。プログラムがクラッシュすることには変わりありません。

<!---->

> もし、`myOPoint`が実際の座標を保持していない場合、`myOPoint.get().x`は`NoSuchElementException`を投げてプログラムをクラッシュさせます。これは元のコードから何一つ良くなってはいません。なぜなら、プログラマの目的はすべてのクラッシュを回避することであって、`NullPointerException`によるクラッシュだけを回避できれば良いわけではないからです。

<!---->

> 繰り返しになりますが、コードはどちらも似たようなもので、`Optional`が従来の参照方法よりも勝っているとは言えません。

![soreha.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/241027/a56e97f3-f50a-db9e-c6c8-8f6e969d8318.jpeg)

まあ,この記事の筆者もたぶん敗北者が多すぎて呆れてこういう記事を書いたのだろう。~~それでもこいつ物知らねえなという記述は散見されるけど。~~

## `Optional#get()`を使わざるを得ないかもしれない例

仕様の関係で**例外が発生しうる処理を外側でハンドリングすることはできない**。

```java
public static int half(int even) throws Exception {
    if (even % 2 == 0); else {
        throw new Exception();
    }
    
    return even / 2;
}

/* ... */

Optional<Integer> someInt = Optional.of(16);

/* できる */
try {
    if (someInt.isPresent()) {
        System.out.println(half(someInt.get()));
    }
} catch (Exception e) {
    System.out.println("偶数を渡してください");
}

/* できない */
try {
    someInt.ifPresent(x -> {
        System.out.println(half(x));
    });
} catch (Exception e) {
    System.out.println("偶数を渡してください");
}
```

これをなんとかするために Qiita でも様々なハックが紹介されている。Either を使うことでもなんとかできると思う。モナドばんじゃい。まあ Java 公式に Either ないんですけどね。