谷本 心 in せろ部屋

はてなダイアリーから引っ越してきました

僕の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()));
}


だいたいこんな感じ、ですかね?