Java8 Stream の使い方を覚えたら、Optional や Either もすぐ使えるようになるよ!編
Integer result =
Arrays.asList(1, 2, 3)
.stream()
.filter(n -> n > 5) //この時点で要素がなくなる
.mapToInt(n -> n * n) //そんなことは意識せずに処理したいことを記述する
.sum();
⇒処理するにあたり、リストの要素があろうとなかろうと意識する必要がない
//戻り値がリストとなる処理があったとして、
List<Integer> someList(Integer n) {
return IntStream.range(1, n)
.mapToObj(i -> Integer.valueOf(i))
.collect(toList());
}
//その関数を利用すると、
List<List<Integer>> listListInt =
Arrays.asList(1, 2, 3)
.stream()
.map(n -> someList(n)) //someList() の戻り値は List<T>
.collect(toList()); //List<List<T>>
⇒リスト要素を処理した戻り値がリストだと List<List<T>> となる
そこで
List<Integer> listInt =
Arrays.asList(1, 2, 3)
.stream()
.flatMap(n -> someList(n).stream()) //List<List<T>> を List<T> に flatten(平らに)する
.collect(toList()); //List<T>
⇒flatMapを使うことでリストのリストを平らにすることができる
- リストモナド的に処理できる
- リスト要素の有無や、結果がリストであることを意識せずに、処理のみを記述すれば良い!
Optional<Integer> result =
Optional.ofNullable(1)
.map(n -> (Integer) null) //この時点でnullになる
.map(n -> n * n); //そんなことは意識せずに処理したいことを記述する
⇒処理するにあたり、値があろうとなかろうと意識する必要がない
Integer anywayResult =
Optional.ofNullable(1)
.map(n -> (Integer) null) //この時点でnullになる
.map(n -> n * n); //そんなことは意識せずに処理したいことを記述する
.orElse(0); //もし途中でNullだったらこの値を返却することとする
⇒途中で値がNullになった際の例外処理は、処理の最後に付与すれば良い
//戻り値がOptionalとなる何らかの処理があったとして、
Optional<Integer> someOption(Integer n) {
return Optional.ofNullable(n);
}
//その関数を利用すると、
Optional<Optional<Integer>> optionOptionInt =
Optional.ofNullable(1)
.map(n -> someOption(n));
//戻り値が Optional<T> なため Optional<Optoinal<T>> となる
⇒値を処理した戻り値が Optional だと、Optional<Optional<T>> となる
そこで
Optional<Integer> optionInt =
Optional.ofNullable(1)
.flatMap(n -> someOption(n));
//Optional<Optional<T>> を Optional<T> に flatten(平らに)する
⇒flatMap を使うことで Optional の Optional を平らにすることができる
Optional<Integer> n1 = Optional.ofNullable(1);
Optional<Integer> n2 = Optional.ofNullable(2);
Optional<Integer> n3 =
n1.flatMap(i1 -> n2.map(i2 -> i1 + i2));
//n1, n2 のいずれか、あるいは両方が null だろうが意識せずに処理したいことを記述すればよい
⇒複数のOptionalを処理する場合でも、それぞれの値の有無を意識する必要がない
- Optionalモナド的に処理できる
- 値の有無や、結果の有無を意識せずに、処理のみを記述すれば良い!
- 要素をフィルタする
list.stream().filter(f)
⇒optional.filter(f)
- 要素を取り出して処理する
list.stream().map(f)
⇒optional.map(f)
- 要素を取り出して処理する(戻り値なし)
list.stream().foreach(f)
⇒optional.ifPresent(f)
- Optionalでは処理結果がnullであることは分かるが、
- nullとなったその理由(例外内容)を知ることができない
- 例外内容に応じた振る舞いを行いたい場合には、Optionalでは役不足
- そこで Either の登場
- Either<Left, Right> という2つの入れ物を持つ
- ただしいずれかひとつしか値を入れることができない
- 慣例として左に例外を、右に正常時戻り値を入れる(正しいのrightにかけている)
- 読みは íːðɚ アクセントが前
*Java8にEitherは含まれていないので、JAVASLANGライブラリを利用
//例外が発生しうる何らかの処理があったとして、
Either<Object, Integer> someEither(Integer i) {
// ほんとはこんな感じの処理
// try {
// //compute...
// return Either.right(1);
// }
// catch (Exception e) {
// return Either.left(e);
// }
return Either.left(new RuntimeException());
}
//その関数を利用するにあたり
Either<Object, Integer> result =
someEither(1) //この時点で例外が発生している
.map(n -> n * 100); //そんなことは意識せずに処理したいことを記述する
⇒例外があろうとなかろうと意識する必要がない
//上の処理の続きとして、
if (result.isRight()) {
System.out.println("Success:" + result.right().get());
} else {
System.out.println("Failure:" + result.left().get());
}
⇒例外有無や例外内容に応じて振る舞いを行う
Integer anywayResult =
someEither(1)
.map(n -> n * 100)
.getOrElse(0); //もし途中で例外発生していたらこの値を返却することとする
⇒途中で例外になった際にデフォルト値があるのであれば最後に付与する
Either<Object, Either<Object, Integer>> eitherEitherInt =
someEither(1)
.map(n -> someEither(n));
⇒値を処理した結果がEitherだと、Either<Either<T>> となるが、
Either<Object, Integer> eitherInt =
someEither(1)
.flatMap(n -> someEither(n));
⇒flatMap を使うことで Either の Either を平らにすることができる
Either<Object, Integer> e1 = someEither(1);
Either<Object, Integer> e2 = someEither(2);
Object e3 =
e1.flatMap(n1 -> e2.map(n2 -> n1 + n2))
.getOrElse(1);
⇒複数の処理の場合でも、それぞれの例外の有無を意識する必要がない
- Eitherモナド的に処理できる
- 例外の有無や、結果が例外であることを意識せずに、処理のみを記述すれば良い!
*JAVASLANGライブラリを利用
//例外が発生しうる何らかの処理があったとして、
Integer someCompute() {
//compute...
throw new RuntimeException();
}
//Tryを使うと処理結果をEither化できる
Either<Throwable, Integer> trySomeCompute =
Try.of(() -> someCompute()).toEither();
そうすると、
//例外の発生を意識せずに処理に集中できる
Integer resultInt =
Try.of(() -> someCompute())
.toEither();
.map(res -> res * 100)
.getOrElse(0);
//エラーになり得る複数の処理を意識せずに記述する
Either<Throwable, Integer> result =
Try.of(() -> 1 / 1)
.mapTry(x -> 1 / 0) //ここで例外となるが意識する必要がない
.mapTry(x -> 1)
.toEither();
- Stream から map, flatmap に慣れれば、
- Optional, Either も同じように処理できるようになるよ
- モナドってむずかしそうだけど、並べてみるとなんとなく雰囲気わかるでしょう
- 何かを意識せずに処理に集中できる仕組み、という感じかな