スマートデバイスアプリ開発のあれやこれや

Optional型活用のすゝめ

はじめに

niwaka.hateblo.jp

 Streamの話を↑でしましたが,コードレビューで指摘する内容として「Optional型を上手く使いましょう」という話もよくしています。Optional型もJava8から登場した比較的新しい文法になりますが,nullを扱う場合に便利になるので積極的に活用していきたいところです。

Optional型=nullかもしれない値

 Optional型はJava8から追加された新しいクラスです。どういうケースで使うといえば,nullを返却する可能性があるメソッドの戻り値型として採用すると良いでしょう。まず,従来の文法で説明してみます。

private MyEntity convertToMyEntity(String name) {
    if (name.equals("Apple")) {
        return new MyEntity("name");
    } else {
        return null;
    }
}

 引数で与えられる文字列が"Apple"の時以外はnullを返します。このメソッドを使う側は,処理結果としてnullが返ってくるかどうかをどうやって判定するのでしょうか? 呼び出し先のソースコードがあるならば,そちらを見れば良いでしょうが対象クラス・メソッドがjarファイルになっているケースもあります。なにより毎回ソースコードを当たらないとnullが返るかどうか分からないというのは面倒です。そういう時に使ってほしいのがOptional型なのです。

private Optional<MyEntity> convertToMyEntity(String name) {
    if (name.equals("Apple")) {
        return Optional.of(new MyEntity("name"));
    } else {
        return Optional.empty();
    }
}

 こんな感じで戻り値型をOptional型でラッピングして返却します。”これ,なんの旨みがあるの?”と思いますよね。Optional型のミソはラッピングされた値を取り出すところにあります。Optional型から元のMyEntity型を取得するコードは以下のようになります。

Optional<MyEntity> entity = convertToMyEntity("Apple");
entity.ifPresent(e -> {
    //e=MyEntity
});

 値を取り出す際にnullチェックを強制出来るという点が最大の特徴です。このルールをチームメンバーで共有しておけば,少なくとも自前で作成したコード部分については

  • Optional型=nullが返ってくる可能性がある
  • 非Optional型=nullは返ってこない

 といった方針でコーディングが出来るのではないでしょうか。※getメソッドでチェックすることなく強制的に中身を取り出すことも出来ますが,例外が飛ぶので禁止しましょう。コードレビューでの注視ポイントだと思います。

 個人的には最初は↑の箇条書きの方針で採用していけば良いと考えていますが,ある程度Optional型に慣れてきたらもう一検討して欲しいです。最初に述べた通り,Optional型は相手にnullチェックを強要出来ます。逆に言うとnullでも構わないようなケースは別にOptional型である必要はないでしょう。nullを扱うためにいちいちアンラップするのも面倒ですから。検査例外と非検査例外……みたいですね。

戻り値以外でOptional型を使うのはNG

 「Optional型,便利じゃん!」ってなってきましたか? 便利なOptional型ですが使う場所は限定しましょう。Optional型に慣れてきた頃にやりがちなミスですが,フィールドの型としてOptional型を使ったりメソッド引数としてOptional型を使うのは止めるべきです。

private Optional<String> option;
    
private void doSomething(Optional<String> args) {
    //なんかの処理
}

 理由は2点です。

  • 再代入できる様式だと考慮すべきパターンが増える
  • 引数でOptional型を与えるメリットが誰にもない

 前者「再代入できる様式だと考慮すべきパターンが増える」はフィールド型として使う場合の話です。再代入可能なOptional型を定義するとそれを参照する側は

  • Optional型オブジェクトは存在するが,中身が空(null)である
  • Optional型オブジェクトは存在するが,中身が入っている
  • Optional型オブジェクトが存在しない(=nullで代入された)

 という3つのパターンを考慮しないといけません。これでは逆にデメリットが増えてしまいます。採用するのであればgetterメソッドの型だけをOptionalにする方が無難でしょう。

 後者「引数でOptional型を与えるメリットが誰にもない」は説明が難しいのですが,メソッドを作る側の立場になって考えてみましょう。引数型がOptional型以外でも結局のところnullチェックって実装するのではないでしょうか? 非Optional型で来たからといって信用できないかなという気がしています。なので,個人的には引数にはOptional型を使わない方が良い?と思っています。基本,Optional型は受け取った場所(メソッド)で中身を精査して扱うというのが王道なのでしょう。

 Oracleの中の人も「Optional型は戻り値だけで使ってねん」と言ってた気がします。

togetter.com