Javaについて徹底解説!

【Optional入門】Javaでnullを扱うベストプラクティスのご紹介

大石 英人

開発エンジニア/Java20年/Java GOLD/リーダー/ボールド歴2年

Javaのjava.util.Optionalは「nullかもしれない値」を上手に取り扱うためのクラスです。Java 8で追加された、まだ比較的新しいクラスです。

「え、このメソッドって戻り値がnullになることがあるの?」ということは、Javaプログラマなら一度は経験があると思います。そういう時に限ってnullチェックを忘れてしまって、バグを作り込んで怒られてしまうのです

Optionalを使うと、そういうミスを事前に防ぐことができます。値がnullかもしれないと意識できますし、nullだった場合にありがちな処理も、大変簡単に書けるのです。

この記事では「Optionalってなにそれ? おいしいの?」な人向けに、Optionalの使い方についてお伝えします。

※この記事のサンプルは、Java 10の環境で動作確認しています。


1.Optionalはnullかもしれないと伝えるもの

Optionalは何かの値を包み込むクラスであり、同時に「値はnullかもしれないよ、気を付けて!」と伝えるためのクラスです。

Optionalはメソッドの戻り値で使われるのが普通ですが、nullかもしれない値に何か処理をしたい時にも便利に使えます。


2.Optionalの使い方

2-1.nullチェックをしないと痛い目を見る例

以下のプログラムがあったとします。getSampleOptionalSampleを取得して、helloメソッドを呼ぶものです。

メソッドgetSampleOptionalSampleのインスタンスが戻ってくるかはランダムです。helloできるかは、まさに運次第。気を付けないとNullPointerExceptionが簡単に発生してしまう、とても危険なプログラムです。

これをどうにかしようとすれば、普通は以下のようにメソッドの戻り値がnullかどうか、if文でチェックします。

でも、こうして戻り値をnullチェックしてくれるかは、このメソッドを「呼び出す」プログラマ次第です。プログラムを一回動かして、上手く動けばそのまま、という人も多いでしょう。でも、この例のように実はnullが戻ることがあるとすれば…?

「メソッドを作る」プログラマは、どうやれば「メソッドを呼び出す」プログラマに「このメソッドはnullが戻るかもしれない」と気付いてもらえるのでしょうか。設計書やJavadocをきちんと書いても、読まない人は全然読みませんよね。正直言って、私もそうです。

これをOptionalでどうにかしてみましょう。

2-2.Optionalを使ってみる

2-2-1.メソッドの戻り値をOptionalにする

では、さっそくOptionalを使ってみます。

まず最初にやることは、値がないかもしれないメソッドの戻り値の型を、戻り値にしたいクラスそのものではなく、Optionalにすることです。

Optionalの型引数(<>でくくってある部分)は、Optionalで包み込みたいクラスを書きましょう。型引数を使えば、何が本当の値なのか呼び出すプログラマに簡単に伝わります。

こうすると、メソッドを呼び出す人は「Optionalが戻るなら、値がないかもしれないんだな」と気付いてくれます。Javaプログラマの間には、Optionalとはそういうものだ、という共通認識があるからです。

2-2-2.Optionalのインスタンスを作る

次に、メソッドからOptionalreturnするために、Optionalのインスタンスの作り方を知りましょう。

Optionalのインスタンスを作るには、Optional.ofOptinal.ofNullableを、包み込みたい値を引数に使います。ofnullでない値専用で、ofNullableは値がnullかもしれない場合に使います。値がnullの場合は、Optional.emptyでもよいですね。

では、サンプルのメソッドを書き換えてみましょう。サンプルでは値は「nullかもしれない」ので、ofNullableを使います。

これでメソッドがOptionalreturnするようになりました。次は、メソッドを呼び出した側の処理を変更します。

2-2-3.Optionalから値を取り出す、値があるかチェックする

Optionalから値を取り出すには、Optional.getをします。getするとOptionalが持っている値を取り出せるのですが、値がないならNoSuchElementExceptionthrowされます。

なお、Optionalで気を付けたいのは、Optionalnullで作った場合でも、nullという値を持っているわけではないことです。Optionalは、あくまで値のあり・なしを管理するという考え方なので、nullという値は直接取り出せないのです。

Optionalからgetした際に例外が発生するのは嫌なので、事前に値があるかをチェックしましょう。値のありなしの確認にはOptional.isPresentを使います。isPresentは、値があるならtrueを戻します。

これでOptional化は一応できましたがうーん、これだけだと、正直言ってOptionalにはいいところがあまりなさそうに見えます。

メソッドから値を直接取得できないので、追加の処理は確かに書くようにはなりました。でも、値がないならそのままgetすると例外が発生しますし、結局if文でのチェックがあるのですから、if文でnullかどうかをチェックするのと変わらないですよね?

そう思ってしまうのは、まだここではOptionalの本当の機能を使っていないからです。Optionalの他のメソッドと組み合わせてこそ、Optionalの便利なところが見えてきます。


3.Optionalを上手に使う

Optionalを上手に使うには、Java 8で追加された関数型インターフェイスやラムダ式の知識が少しだけ必要です。でも、怖がる必要はありません!! 簡単なことをするクラスを作るだけです。

Optionalを使うメリットの一つは、nullチェックのif文や続くelseを、プログラマが直接書かないで済むことです。処理の書き忘れを防止する上では、「書かなくてもいい」ということそのものが、大変意味のあることなのです。

3-1.値がある時だけ処理をするifPresent/ifPresentOrElse

値がある時だけ処理をすることは良くあります。普通はif文を使いますが、OptionalにはそのためのメソッドifPresentがあります。

if文とgetがなくなったので、スッキリしました。値がある場合の処理だけをConsumerで書けばいいのですね。値がない場合は何もされません。

ifPresentOrElseは、少しパワーアップしたifPresentです。値がない場合のelseの処理を、二つ目の引数のRunnableで書けるのです。

3-2.値がない時にデフォルト値を指定できるorElseorElseGet

値がない時のデフォルト値を指定したいなら、Optional.orElseを使いましょう。orElseifelseelse部分を補ってくれます。

デフォルト値をnullにしたいなら、orElseの引数にnullを指定すればOKです。

orElseのいいところは、ここでもif文を書かずに済んでいることです。でも、もっと大事なのはOptionalのメソッド一つを呼ぶだけで、後続の処理はnullかどうかを全く意識しなくて済むことです。

デフォルト値を作るロジックが少々複雑な場合は、Optional.orElseGetを使いましょう。こちらでは複数行からなる処理を、Supplierのラムダ式で渡せます。

なお、このサンプルでは自分で作ったクラスを使っていますが、StringIntegerでも同じことができますよ。

3-3.値がある時だけ値を変換するmap

値がある時だけ、値を何かに変換したいことがあります。その場合はOptional.mapを使います。

mapした結果は、またもやOptionalになります。なぜかと言うと、変換後の値があるかわからないからです。値がないなら、値を持たないOptionalが戻ります。

mapしたOptionalを使って何かをしたいなら、前述したifPresentorElseを合わせて使いましょう。メソッドチェインを使うと、プログラムがよりスッキリします。こうすると、やりたいことがif文を解読するよりも読み取りやすいと思いませんか?

3-4.条件にマッチした値だけにするfilter

Optionalが値を持っており、また値が特定の条件を満たす時のみ取り出したいなら、Optional.filterを使いましょう。

filterした結果は、mapと同様にOptionalです。filterの条件にマッチするなら値があるOptionalで、マッチしないなら値を持たないOptionalです。

3-5.値変換の結果がOptionalの場合に便利なflatMap

mapでOptionalを戻したい場合に使えるのがOptional.flatMapです。mapOptionalに変換すると、mapの戻り値がOptional<Optional<?>>というようにOptionalの入れ子になってしまい、大変使いづらいです。

flatMapを使うと、Optionalが入れ子にならないようにmapできます。つまり、Optionalの値を取り出して、新しいOptionalを作ってくれるのです。しかも、値の有無はそのままにしてくれます。

3-6.値でStreamを作るstream

Optionalが持っている値から直接Streamを作れます。Optionalが持っているのがnullの場合は、空のStreamです。

streamは、Optionalの値がStreamを作れるようなクラス(Listなど)だと、Stream.flatMapと組み合わせて便利に使えたりします。

このように、値をOptionalから取り出す処理をいちいち書かなくても、安全に処理できるStreamが作れるのは便利ですね。


4.まとめ

この記事では、Java 8から追加されたOptionalをお伝えしました。Optionalは、nullかもしれないモノを扱うためのクラスで、メソッドの戻り値として使うことが主に想定されています。

メソッドがOptionalを戻すと、メソッドを呼ぶ人が「このメソッドはnullを戻すかもしれないのか」と気付きやすくなります。これにより、予期せぬNullPointerExceptionの発生を少なくできるのです。

そしてOptionalを使えば、普通の処理ではif文を書かなければできない良くある処理を、Optionalのメソッド呼び出しで書けるようになります。プログラムの行数は減りますし、なによりやろうとしていることが明確に表現できるのです。

『技術力』と『人間力』を高め市場価値の高いエンジニアを目指しませんか?

私たちは「技術力」だけでなく「人間力」の向上をもって遙かに高い水準の成果を出し、関わる全ての人々に感動を与え続ける集団でありたいと考えています。

高い水準で仕事を進めていただくためにも、弊社では次のような環境を用意しています。

  • 定年までIT業界で働くためのスキル(技術力、人間力)が身につく支援
  • 「給与が上がらない」を解消する6ヶ月に1度の明確な人事評価制度
  • 平均残業時間17時間!毎週の稼動確認を徹底しているから実現できる働きやすい環境

現在、株式会社ボールドでは「キャリア採用」のエントリーを受付中です。

まずは以下のボタンより弊社の紹介をご覧いただき、あなたの望むキャリアビジョンをエントリーフォームより詳しくお聞かせください。