こんにちは。
こちらの記事にご興味を持ってくださりありがとうございます。
さて、今回は私が3年ほど触っているJavaの初心者殺し二大巨頭の一つ、
Optionalで知っておいたほうが良いことを書き記しておきます。
ちなみにもう一つはラムダ式(Stream API)だと思っています。(ここでもラムダ式使ってしまいますがご容赦を…)
実装をみたりたくさんの記事を見たりしている中で、
Optinalはメソッドでnullチェックできる、なんならしなくてよいものだと
感じている人が多いように日々感じています。
そうじゃない!と言いたいと思って筆を執りました。
そもそもOptionalとは
final Optional<String> testStringOpt = getTestString();
final String testString = testString.orElse("値無し");
private Optional<String> getTestString() {
return Optional.ofNullable(testRepository.findById(1));
}
Optionalをつけることで、
ソースコードの前後を見なくとも
この変数の中にはnullの可能性が
あることを明示できます。
これ孫の手くらいの革命じゃないですか?!
変にnullにビビらなくてよいので、実装スピードが格段に上がります。
だから私は
「気遣いの実装」
と呼んでいます。
Repositoryから取得するときなどで
使用されることが多いです。
また特に素晴らしいのが、
例外処理の記述が減るもしくは
Optionalの影響によりエラー時の挙動を
操作しやすい点です。
ありがちなOptionalと代替案
その1 nullチェック
final Optional<Integer> testIntegerOpt = Optional.of(1);
if (testIntegerOpt.isPresent()) {
final Integer testInteger = testIntegerOpt.get() + 1;
}
確かにnullチェックしたいですよね。
そのためのOptionalですもんね。
…じゃなくて!
Optionalはnullを扱うプロフェッショナルなので、
nullを上手にキレイなコードで書くためのメソッドが搭載されています。
final Optional<Integer> testIntegerOpt = Optional.of(1);
testIntegerOpt.ifPresent(integer -> {
final Integer testInteger = integer + 1;
});
これ、やっていることは全く同じです。
厳密にはifPresent特有の問題などあり、すべてが同じようにできるわけではありません。
ネストが深くなりすぎることを防いだり、if()if()if()...みたいな条件分岐地獄からも足を洗うことができます。
ちなみに、elseの分岐を書けないじゃないか!やっぱりif()で書いたほうがいいな!と思っているそこのあなた!
こんなメソッドがJava11から実装されました。
final Optional<Integer> testIntegerOpt = Optional.of(1);
testIntegerOpt.ifPresentOrElse(integer -> { // ifの部分
final Integer testInteger = integer + 1;
}, () -> { // elseの部分
final Integer testInteger = 1 * 2;
});
上記処理だと、testIntegerOptがあればintegerに1を足す処理をして、そうでなければ1に2をかける流れです。
その2 ムダな記述
Optionalはnullを扱うプロフェッショナルなので、Optionalのメソッドは事実上のnullチェックをしてくれていることがあります。
final Test test = getTest();
Optional.ofNullable(test.getTestString()).ifPresentOrElse(string -> {
System.out.println(string);
}, () -> System.out.println("値がありません。"));
private Test getTest() {
final Test test = new Test();
test.setTestString(null);
return test;
}
こんな実装をしたとします。
一見良さそうですよね。
でもこれ、もっと短く見やすくできます。
ネストが深くなることすらありません。
final Optional<Test> test = getTest();
final String string = Optional.of(test).map(Test::getTestString).orElse("値がありません。");
System.out.println(string);
private Test getTest() {
final Test test = new Test();
test.setTestString(null);
return test;
}
どこにもnullチェックが入っていないのにもかかわらず、mapを使うことで、内部でnullチェックをしてくれています。
また、似たようなメソッドでflatMapがあります。
flatMapはmapと違い、orElse(Optional.empty())固定で返すメソッドです。
ここは使い分けですね。
ちなみにこの「::」はメソッド参照といって、test.getTestString()でいう「.」の役割と同じようなものです。
今までだったらif()を使って何行にも渡って書いていたものがわずか一行です。
しかもプログラミングをしていて、意外とやっかいなスコープ問題も一気に解消されています。
Optionalがnullを扱うプロフェッショナルということがおわかりいただけたでしょうか?
おまけ
冒頭で述べた例外処理の記述が
シンプルになる話をします。
Optional<String> stringOpt = Optional.empty();
try {
stringOpt = getTestString(1);
} catch (Exception e) {
stringOpt = Optional.empty();
}
return stringOpt.isPresent();
// 例外を投げるには微妙なメソッドですが…例外を投げる箱だと思ってください。
private Optional<String> getTestString(final Integer integer) throws Exception {
return Optional.ofNullable(testRepository.findById(integer).getTestString());
}
Optionalを使用すればnullである可能性を明示しながら
このように例外処理も手中に収めることができます。
最初はとっつきづらいですが、少し慣れれば
素晴らしい味方となってくれる頼りになる存在、
それがOptionalです。
一つのことを扱い、最初は少しとっつきづらいけど
とても頼りになるステキなものOptional、
なんだか職人さんみたいですよね。
カッコ良いです。
職人さんと仲良くなってみてくださいね。
私ももっと親睦を深めます。