導入
Factory Methodのレッスンで、存在しない形状を渡すと例外を投げるようにしました。でも「見つからなかった」ことを、必ずしも例外で表現する必要はありません。特に「検索して見つからないこともある」ような処理では、null を返す設計がよく使われてきましたが、null は「呼び出し側がチェックし忘れる」という事故のもとです。Optional は、「値があるかもしれないし、ないかもしれない」という状態そのものを型で表現します。
説明
import java.util.Optional;
import java.util.List;
class ShapeRepository {
private List<Shape> shapes;
Optional<Shape> findByName(String name) {
for (Shape s : shapes) {
if (s.name.equals(name)) {
return Optional.of(s); // 見つかった
}
}
return Optional.empty(); // 見つからなかった
}
}
// 呼び出し側
Optional<Shape> found = repository.findByName("circle");
if (found.isPresent()) {
System.out.println(found.get().name);
} else {
System.out.println("見つかりませんでした");
}
// より簡潔に
String name = repository.findByName("circle")
.map(s -> s.name)
.orElse("不明な形状");
classDiagram
class Optional~Shape~ {
+of(shape) Optional
+empty() Optional
+isPresent() boolean
+get() Shape
+orElse(fallback) Shape
}
Optional<Shape>を戻り値の型にすることで、「この呼び出しは見つからないこともある」ことがメソッドのシグネチャ(型)だけで分かります。nullを返す設計だと、そのことはコメントを読まないと分かりません。Optional.of(s)… 値がある場合のOptional。Optional.empty()… 値がない場合のOptional。.orElse("不明な形状")… 値がなかったときの代わりの値をその場で指定できます。if/elseで毎回チェックするより簡潔です。- Factory Methodパターンで「見つからない場合は例外を投げる」実装をしましたが、「見つからないこと自体が正常な結果でありうる」場面(検索・検証など)では、例外より
Optionalの方が向いていることがあります。「異常だから例外」「あり得る結果の1つだからOptional」と使い分けるのが基本の指針です。
まとめ
この章で見たように、モダンJavaの言語機能はデザインパターンを「不要にする」わけではなく、パターンが解決しようとしていた問題そのものへの理解があってこそ、言語機能の価値も実感できます。sealed はFactory Methodが抱える「実装の網羅性」の課題に、ラムダはStrategyの「1行のためのクラス」という重さに、それぞれ言語仕様の側から応えたものです。次に新しいJavaのバージョンで機能が追加されたときも、「これはどのパターンの、どんな課題を解決しているのか」という視点で見てみてください。