僕のOptional<>の使い方がカッコワルイ。
昨日、Spring MVCのOptionの使い方がカッコイイという話で、こんなコードを書きました。
@RequestMapping("/") public String hello(@RequestParam("name") Optional<String> name) { if (name.isPresent()) { return "Hello, " + name.get(); } else { return "Hello, John"; } }
そしたら、多方面から切れ味するどいツイートが飛んできました
あー。
そんなわけで、今回の例で言えば、こう書くのがカッコイイと思います。
@RequestMapping("/") public String hello(@RequestParam("name") Optional<String> name) { return "Hello, " + name.orElse("John"); }
あるいは、値がない場合の代替値が決まっているのであれば、defaultValueが使えます。
@RequestMapping("/") public String hello(@RequestParam(value = "name", defaultValue = "John") String name) { return "Hello, " + name; }
しかし実案件では、こういう代替値が決まっている場合より、
引数の有無で処理そのものが変わるパターンが多いように思います。
そういう場合はmapとorElseGetで処理を分けるのがよいみたいです。
この辺りからRequestParamとかあんまり関係なくなってくるので、
アノテーションはまるっと省略します。
mapとorElseGetを使って処理を分ける場合、こんな書き方になります。
public String hello(Optional<String> name) { return name.map(n -> foo(n)).orElseGet(() -> bar()); }
なんか、ちょっといい感じになりました!
ただ1変数だとまだ見やすいのですが、2変数になる場合はどうしましょう。
とりあえずif文で全部書いてみます。
public String hello(Optional<String> name1, Optional<String> name2) { if (name1.isPresent()) { if (name2.isPresent()) { return call(name1.get(), name2.get()); } else { return call1(name1.get()); } } else { if (name2.isPresent()) { return call2(name2.get()); } else { return call(); } } }
ちょっと読みにくい、ぐらいですかね。
mapとorElseGetで書いてみます。
public String hello1(Optional<String> name1, Optional<String> name2) { return name1.map(s1 -> name2.map(s2 -> call(s1, s2)).orElseGet(() -> call1(s1))) .orElseGet(() -> name2.map(s2 -> call2(s2)).orElseGet(() -> call())); }
ちょっとわけわからん感じになりましたが、行数は相当減りました!
じゃぁ間を取って、片方をif、片方をmapで書いてみます。
public String hello2(Optional<String> name1, Optional<String> name2) { if (name1.isPresent()) { String s1 = name1.get(); return name2.map(s2 -> call(s1, s2)).orElseGet(() -> call1(s1)); } else { return name2.map(s2 -> call2(s2)).orElseGet(() -> call()); } }
やらなきゃ良かった感のあるソースになりました。
やっぱりmapとorElseを組み合わせて、
あとはインデントを入れて可読性をあげれば良いんでしょうか。
public String hello1(Optional<String> name1, Optional<String> name2) { return name1.map(s1 -> name2.map(s2 -> call(s1, s2)) .orElseGet(() -> call1(s1))) .orElseGet(() -> name2.map(s2 -> call2(s2)) .orElseGet(() -> call())); }
だいたいこんな感じ、ですかね?