当サイトは、アフィリエイト広告を利用しています

【java8】Optionalの仕組みと使い方

作成日:2023月02月25日
更新日:2023年09月07日

java8のOptionalを使ってみたので
javaにおけるOptionalとは何で、どんなメソッドがあり
どう使用するのかについて自分なりにまとめてみる。

下記の書籍が参考になった

Optionalとは?

Optionalは値をラップして生成し、その値がnullかもしれない状態にするクラス。
※なんか量子力学みたい
Optionalクラスから生成されたOptinalインスタンスは「nullかもしれない状態」のため
取り出す処理をしてやらないと他の処理で使うことはできなくなる。
コンテナ・オブジェクトと呼ばれるらしい。

Optionalインスタンスの生成方法

Optionalインスタンスの生成方法は下記の3つがある

  • Optional.of()
  • Optional.empty()
  • Optional.ofNullable()

3つあるが実際よく使うのは「Optional.ofNullable()」になる。
というか他のやつはほぼ使わない。。

Optional.of()

引数に設定した値をラップしてOptionalインスタンスを生成する。

of()
import java.util.*;
public class Main {
public static void main(String[] args) throws Exception {
String str = "hoge";
Optional<String> nullofStr = Optional.of(str);
System.out.println(nullableStr);
}
}
// 実行結果
// Optional[hoge]

注意点としては引数の値がnullの場合は
NullPointerExceptionが発生する

of()エラー
import java.util.*;
public class Main {
public static void main(String[] args) throws Exception {
String str = null;
Optional<String> nullofStr = Optional.of(str);
}
}
// 実行結果
// Exception in thread "main" java.lang.NullPointerException

nullで落ちるのでほとんど使うことはない。
※少なくとも今まで自分は使ったことないです。

Optional.empty()

空をのOptionalインスタンスを生成する。

empty()
import java.util.*;
public class Main {
public static void main(String[] args) throws Exception {
Optional<String> empty = Optional.empty();
System.out.println(empty);
}
}
// 実行結果
// Optional.empty

使ったことないな。。。

Optional.ofNullable()

引数の値からnullかもしれない状態のOptionalインスタンスを生成する。
値がnullだった場合は空を表すOptionalのemptyが生成される。

ofNullable()
import java.util.*;
public class Main {
public static void main(String[] args) throws Exception {
String str = null;
Optional<String> nullableStr = Optional.ofNullable(str);
System.out.println(nullableStr);
String str2 = "hoge";
Optional<String> nullableStr2 = Optional.ofNullable(str2);
System.out.println(nullableStr2);
}
}
// 実行結果
// Optional.empty
// Optional[hoge]

値がnullであってもNullPointerExceptionは発生しないので
通常はこれを使う。

Optionalインスタンスから値を取り出す方法

Optionalインスタンスは「nullかもしれない状態」なので
その値を取り出すまで基本使うことはできない。
取り出すメソッドは下記がある(java8で使えるやつ)

  • get
  • orElse
  • orElseGet
  • orElseThrow
  • isPresent
  • ifPresent

詳しくはクラスOptional< T >参照

get

Optionalインスタンスが保持している値を取得する

get
import java.util.*;
public class Main {
public static void main(String[] args) throws Exception {
String str = "hoge";
Optional<String> nullableStr = Optional.ofNullable(str);
System.out.println(nullableStr.get());
}
}
// 実行結果
// hoge

値がnullだった場合、NoSuchElementExceptionが発生してしまうので
あまり使わない

NoSuchElementException
import java.util.*;
public class Main {
public static void main(String[] args) throws Exception {
String str = null;
Optional<String> nullableStr = Optional.ofNullable(str);
System.out.println(nullableStr.get());
}
}
// 実行結果
// java.util.NoSuchElementException: No value present

使う場合は後述するisPresent()とあわせて使う

get_isPresent
import java.util.*;
public class Main {
public static void main(String[] args) throws Exception {
String str = null;
Optional<String> nullableStr = Optional.ofNullable(str);
if(nullableStr.isPresent()){
System.out.println(nullableStr.get());
}else{
System.out.println("値なし");
}
}
}
// 実行結果
// 値なし

orElse

Optionalインスタンスの値を取得する。
もし値がemptyだった場合、引数で設定したデフォルト値を返す

orElse
import java.util.*;
public class Main {
public static void main(String[] args) throws Exception {
//null
String str = null;
Optional<String> nullableStr = Optional.ofNullable(str);
System.out.println(nullableStr.orElse("デフォルト値"));
//値あり
String str2 = "hoge";
Optional<String> nullableStr2 = Optional.ofNullable(str2);
System.out.println(nullableStr2.orElse("デフォルト値"));
}
}
// 実行結果
// デフォルト値
// hoge

orElseメソッドを一番よく使う。
特にstreamAPIのfindFirstの戻り値として使うことが多い。

orElseGet

orElseの戻り値に関数型インターフェースのSupplierを指定できる。

orElseGet
import java.util.*;
public class Main {
public static void main(String[] args) throws Exception {
//null
String str = null;
Optional<String> nullableStr = Optional.ofNullable(str);
System.out.println(nullableStr.orElseGet(()->"デフォルト値:" + "aaaaaa"));
//値あり
String str2 = "hoge";
Optional<String> nullableStr2 = Optional.ofNullable(str2);
System.out.println(nullableStr2.orElseGet(()->"デフォルト値"));
}
}
// 実行結果
// デフォルト値:aaaaaa
// hoge
  • Supplierなので引数なしで戻り値だけある関数を渡せる。
  • orElseと違って値ではなく関数で処理をかける

orElseThrow

orElseのnullだった場合にExceptionを発生させるversion

orElseThrow
import java.util.*;
public class Main {
public static void main(String[] args) throws Exception {
//null
String str = null;
Optional<String> nullableStr = Optional.ofNullable(str);
System.out.println(nullableStr.orElseThrow(Exception::new));
}
}
//実行結果
// java.lang.Exception

isPresent

Optionalインスタンスがemptyかどうかを判定する。
値がある場合はtrue,emptyの場合はfalseを返却する

isPresent
import java.util.*;
public class Main {
public static void main(String[] args) throws Exception {
//null
String str = null;
Optional<String> nullableStr = Optional.ofNullable(str);
System.out.println(nullableStr.isPresent());
//値あり
String str2 = "hoge";
Optional<String> nullableStr2 = Optional.ofNullable(str2);
System.out.println(nullableStr2.isPresent());
}
}
// 実行結果
// false
// true

ifPresent

Optionalインスタンスの値がある時のみ引数に設定した関数(Consumer)
を実行させる

ifPresent
import java.util.*;
public class Main {
public static void main(String[] args) throws Exception {
//null
String str = null;
Optional<String> nullableStr = Optional.ofNullable(str);
nullableStr.ifPresent((val)->System.out.println(val));
//値あり
String str2 = "hoge";
Optional<String> nullableStr2 = Optional.ofNullable(str2);
nullableStr2.ifPresent((val)->System.out.println(val));
}
}
// 実行結果
// hoge
  • orElseGetと違い、値がある時のみ処理させたい場合はこっちを使う。
  • 値がemptyの場合、処理は何も実行されない。
  • 関数はConsumerのため引数を渡せるが戻り値を帰ってこない。

Optionalのつかいどころ

Optionalを使うメリットととしては

  • メソッドの戻り値をOptionalにすればnullチェック強制できる
  • 取得した値がnullのときのデフォルト値をできる

だと思う。 自分がOptionalを使うのはこれぐらいなのですが
もっと良い活用方法があるかもです。。

メソッドの戻り値をOptionalにすればnullチェック強制できる

streamAPIのfindFirstやreduce(引数が一つのver)でもそうなっているが
関数やメソッドの戻り値のがnullである可能性がある場合に戻り値を
Optionalに設定することで、呼び出し側はOptionalの取り出し処理を
書く必要がある。
そのため必然的にnull対応を入れることになる。
サンプルを書いてみる

Optionalを使わないパターン

ランダムでPersonオブジェクトかnullを返すSupplier関数を作成し、
その関数実行結果からPersonオブジェクトのnameを取得して表示するサンプル。

java
import java.util.*;
import java.util.stream.*;
import java.util.function.Supplier;
public class Main {
public static void main(String[] args) throws Exception {
//nullかPersonオブジェクトを返す関数
Supplier<Person> random = () -> {
Random rnd = new Random();
//booleanをランダムで作る
if(rnd.nextBoolean()){
Person pobj = new Person(1,"takashi","東京1番地",20);
return pobj;
}else{
return null;
}
};
//実行する
System.out.println(random.get().getName());
}
}
// 実行結果
// takashi もしくは NullPointerException

関数でnullが返却された場合はNullPointerExceptionが発生してしまう。

【java8】OptionalSample_Optionalなし

Optionalを使うパターン

Supplier関数の戻り値をOptionalに変更し、
呼び出し元でifPresentでnull対応する。

sample_ifPresent
import java.util.*;
import java.util.stream.*;
import java.util.function.Supplier;
public class Main {
public static void main(String[] args) throws Exception {
//nullかPersonオブジェクトを返す関数
Supplier<Optional<Person>> random = () -> {
Random rnd = new Random();
//booleanをランダムで作る
if(rnd.nextBoolean()){
Person pobj = new Person(1,"takashi","東京1番地",20);
return Optional.ofNullable(pobj);
}else{
return Optional.empty();
}
};
//ifPresentを使ってnull対応
random.get().ifPresent((obj)->System.out.println(obj.getName()));
}
}
// 実行結果
// 出力なし もしくは takashi
  • ifPresentのためrandom関数の戻り値がある時のみnameが出力される

orElseGetでnull対応する場合

orElseGet_sample
import java.util.*;
import java.util.stream.*;
import java.util.function.Supplier;
public class Main {
public static void main(String[] args) throws Exception {
//nullかPersonオブジェクトを返す関数
Supplier<Optional<Person>> random = () -> {
Random rnd = new Random();
//booleanをランダムで作る
if(rnd.nextBoolean()){
Person pobj = new Person(1,"takashi","東京1番地",20);
return Optional.ofNullable(pobj);
}else{
return Optional.empty();
}
};
//orElseGetでnull対応
System.out.println(random.get().orElseGet(()->{
Person pobj = new Person(0,"default","",99);
return pobj;
}));
}
}
// 実行結果
// {1 takashi 東京1番地 20} もしくは {0 default 99}

orElseGetでnull対応した場合はrandom関数でオブジェクトが返却された場合は
はその値が、nullの場合はorElseGetで設定したデフォルト値が出力される。

orElseでnull対応する場合は

orElse_sample
import java.util.*;
import java.util.stream.*;
import java.util.function.Supplier;
public class Main {
public static void main(String[] args) throws Exception {
//nullかPersonオブジェクトを返す関数
Supplier<Optional<Person>> random = () -> {
Random rnd = new Random();
//booleanをランダムで作る
if(rnd.nextBoolean()){
Person pobj = new Person(1,"takashi","東京1番地",20);
return Optional.ofNullable(pobj);
}else{
return Optional.empty();
}
};
//orElseでnull対応
System.out.println(random.get().orElse(new Person(0,"default","",99)));
}
}
// 実行結果
// {1 takashi 東京1番地 20} もしくは {0 default 99}

上記三つのサンプル実装結果をのせます。
適宜コメントアウトして実行してみてください

optional

取得した値がnullのときのデフォルト値をできる

たとえばオブジェクトの文字列プロパティを数値に変換して集計する
場合などはnullがあるとExceptionになる

Optionalを使わない場合

Personオブジェクトリストのageの合計を出すために
mapメソッドでageをint変換して計算する

recuceで集計_Optionalなし
import java.util.*;
import java.util.stream.*;
public class Main {
public static void main(String[] args) throws Exception {
List<Person>pList = new ArrayList<>();
pList.add(new Person(1,"default","住所1","10"));
pList.add(new Person(2,"default","住所1","20"));
pList.add(new Person(3,"default","住所1","30"));
pList.add(new Person(4,"default","住所1",null));//nullがある
pList.add(new Person(5,"default","住所1","50"));
// NumberFormatExceptionが発生
int sumAge = pList.stream().map(p->Integer.parseInt(p.getAge()))
.reduce(0,(accum,value)->accum + value);
}
}
// 実行結果
// NumberFormatException

Personオブジェクトリストのageの合計を出力しようとしているが
nullがあるとNumberFormatExceptionが発生する。

Optionalを使う

mapメソッドでoptionalを使ってnull対応を入れる。

recuceで集計_Optional
import java.util.*;
import java.util.stream.*;
public class Main {
public static void main(String[] args) throws Exception {
List<Person>pList = new ArrayList<>();
pList.add(new Person(1,"default","住所1","10"));
pList.add(new Person(2,"default","住所1","20"));
pList.add(new Person(3,"default","住所1","30"));
pList.add(new Person(4,"default","住所1",null));
pList.add(new Person(5,"default","住所1","50"));
int sumAge2 = pList.stream()
.map(p->{
//Optionalでnullの場合のデフォルト値を設定する
Optional<String> nullableStr = Optional.ofNullable(p.getAge());
return Integer.parseInt(nullableStr.orElse("0"));
})
.reduce(0,(accum,value)->accum + value);
System.out.println(sumAge2);
}
}
// 実行結果
// 110

ageがnullの場合は"0"に置き換わっているため、NumberFormatExceptionは発生しない。 実行ソースをのせる

Optional_reduce

参考

新着記事

タグ別一覧
top