覚えたら書く

IT関係のデベロッパとして日々覚えたことを書き残したいです。twitter: @yyoshikaw

java.util.Optionalに対する理解

JavaのOptional(java.util.Optional)がどういうものであるかについて自分なり理解したので、それについてメモしておきます。

自分なりの理解ということで、細かい部分で正しくないところもあるかもしれません。


Optionalの役割

  • Optionalは、値があるかもしれないし無い(null)かもしれない ことを表現するための型
  • nullに対してより安全な操作を提供する


Optionalをどういう方針で操作すればよいのか

  • 値が存在するならばXXXという処理をする、値が存在しないならばYYYという処理をする という風に、値の存在有無によって実施する内容を明確にした指示をする
  • 値が存在する場合の処理と値が存在しない場合の処理を同時に指示する必要がある(別々に指示しない)
  • 値の存在有無が分からない状態での操作(値取得etc)は基本的にやるべきではない


どういう操作ができるか

例えば以下のような操作が提供される

  • Optional#ifPresent
    • 値が存在すれば指定された処理を実行する(値が存在しなければ何もしない)
  • Optional#filter
    • 値が存在していてかつ引数の条件に一致する場合は元のOptionalを返す、値が存在しない場合や値は存在するが条件に一致しない場合は空のOptionalを返す
  • Optional#orElse
    • 値が存在すればその値を返し、存在しなければ引数で指定した値を返す
  • Optional#orElseGet
    • 値が存在すればその値を返し、存在しなければ引数で指定した値の取得処理を呼び出しそれにより得られた結果を返す
  • Optional#orElseThrow
    • 値が存在すればその値を返し、存在しなければ引数で指定した例外をスローする


使ってはいけないメソッド

以下のメソッドを使うとOptionalに課せられたはずの役割を果たすことができないので、基本的に使うべきではない

  • Optional#get
    • 値の存在有無にかかわらず値を取得しようとする
  • Optional#isPresent
    • 値の存在有無をチェックするだけで、値が存在すれば何をするのか、値が存在しなければ何をするのかという指示に至っていない


サンプル利用シーン

あるデータストアからRepositoryクラスを利用してデータを取得するが、Repositoryクラスはデータがあればそのデータを返すが、存在しない場合はnullを返す。
このような場面でのOptionalの一利用方法を以下に記載します。


以下のようなUserクラスが仮に存在したとします

public class User {

    private final String id;

    private final boolean isEnabled;

    User(String id, boolean isEnabled) {
        this.id = id;
        this.isEnabled = isEnabled;
    }

    public String id() {
        return id;
    }

    public boolean isEnabled() {
        return isEnabled;
    }

}

上記のUserが複数格納されたデータストアを操作するためのUserRepositoryというクラスが存在していて以下の仕様であるとします。

  • UserRepository#findById は 引数のidに対応するUserを返す
  • UserRepository#findByIdは、紐づくUserがない場合は null を返す

という場合に、UserRepository#findByIdの戻り値をOptionalを使って以下のように処理するコードが書けます

import java.util.Optional;


UserRepository userRepository = ...;

User user =
        Optional.ofNullable(userRepository.findById(id))
                .filter(u -> u.isEnabled())
                .orElseThrow(() -> new UserNotFoundException("user is not found or disable."));

System.out.println("user: " + user);

動作としてはUserRepository#findByIdの戻り値に応じて以下のようになります

  • Userのインスタンスがreturnされた、かつ、有効なUserであった
    • ⇒ Userの内容がコンソールに出力される
  • Userのインスタンスがreturnされた、かつ、無効なUserであった
    • ⇒ UserNotFoundExceptionがスローされる
  • nullがreturnされた
    • ⇒ UserNotFoundExceptionがスローされる


というわけで、java.util.Optionalに関する自分なりの簡単な整理でした。