当サイトは、アフィリエイト広告を利用しています
java8のOptionalを使ってみたので
javaにおけるOptionalとは何で、どんなメソッドがあり
どう使用するのかについて自分なりにまとめてみる。
下記の書籍が参考になった
Optionalは値をラップして生成し、その値がnullかもしれない状態にするクラス。
※なんか量子力学みたい
Optionalクラスから生成されたOptinalインスタンスは「nullかもしれない状態」のため
取り出す処理をしてやらないと他の処理で使うことはできなくなる。
コンテナ・オブジェクトと呼ばれるらしい。
Optionalインスタンスの生成方法は下記の3つがある
3つあるが実際よく使うのは「Optional.ofNullable()」になる。
というか他のやつはほぼ使わない。。
引数に設定した値をラップしてOptionalインスタンスを生成する。
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が発生する
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インスタンスを生成する。
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
使ったことないな。。。
引数の値からnullかもしれない状態のOptionalインスタンスを生成する。
値がnullだった場合は空を表すOptionalのemptyが生成される。
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インスタンスは「nullかもしれない状態」なので
その値を取り出すまで基本使うことはできない。
取り出すメソッドは下記がある(java8で使えるやつ)
詳しくはクラスOptional< T >参照
Optionalインスタンスが保持している値を取得する
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が発生してしまうので
あまり使わない
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()とあわせて使う
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("値なし");}}}// 実行結果// 値なし
Optionalインスタンスの値を取得する。
もし値がemptyだった場合、引数で設定したデフォルト値を返す
import java.util.*;public class Main {public static void main(String[] args) throws Exception {//nullString 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の戻り値として使うことが多い。
orElseの戻り値に関数型インターフェースのSupplierを指定できる。
import java.util.*;public class Main {public static void main(String[] args) throws Exception {//nullString 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
orElseのnullだった場合にExceptionを発生させるversion
import java.util.*;public class Main {public static void main(String[] args) throws Exception {//nullString str = null;Optional<String> nullableStr = Optional.ofNullable(str);System.out.println(nullableStr.orElseThrow(Exception::new));}}//実行結果// java.lang.Exception
Optionalインスタンスがemptyかどうかを判定する。
値がある場合はtrue,emptyの場合はfalseを返却する
import java.util.*;public class Main {public static void main(String[] args) throws Exception {//nullString 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
Optionalインスタンスの値がある時のみ引数に設定した関数(Consumer)
を実行させる
import java.util.*;public class Main {public static void main(String[] args) throws Exception {//nullString 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
Optionalを使うメリットととしては
だと思う。
自分がOptionalを使うのはこれぐらいなのですが
もっと良い活用方法があるかもです。。
streamAPIのfindFirstやreduce(引数が一つのver)でもそうなっているが
関数やメソッドの戻り値のがnullである可能性がある場合に戻り値を
Optionalに設定することで、呼び出し側はOptionalの取り出し処理を
書く必要がある。
そのため必然的にnull対応を入れることになる。
サンプルを書いてみる
ランダムでPersonオブジェクトかnullを返すSupplier関数を作成し、
その関数実行結果からPersonオブジェクトのnameを取得して表示するサンプル。
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が発生してしまう。
Supplier関数の戻り値をOptionalに変更し、
呼び出し元でifPresentでnull対応する。
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
orElseGetでnull対応する場合
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対応する場合は
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}
上記三つのサンプル実装結果をのせます。
適宜コメントアウトして実行してみてください
たとえばオブジェクトの文字列プロパティを数値に変換して集計する
場合などはnullがあるとExceptionになる
Personオブジェクトリストのageの合計を出すために
mapメソッドでageをint変換して計算する
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が発生する。
mapメソッドでoptionalを使ってnull対応を入れる。
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は発生しない。 実行ソースをのせる