LoginSignup
26
24

More than 5 years have passed since last update.

if文でも三項演算子でもない第三のIf文を書いてみる

Last updated at Posted at 2017-04-19

何を言ってるか分からないと思いますが、ちゃんと説明します。

こんなコードを書かなきゃいけないときって思わず眉間に皺が寄りますよね

String s = null;
if (testSomeCond()) {
    s = "is true";
} else {
    s = "is false";
}
doSomething(s);

こんなとき役に立つのが三項演算子で

String s = testSomeCond() ? "is true" : "is false";
doSomething(s);

みたいな感じで書けます。

この三項演算子ですが、可読性が良いとか悪いとかでしばしば論争が起きたりしていて、Go言語とかだとばっさりカットされてたりします。
個人的には三項演算子好きですが、条件や戻り値部分が長くなると確かに読みづらいなと感じます。

そこでこんな書き方はどうでしょう

String s = If.correct(testSomeCond()).then("is true").orElse("is false");
doSomething(s);

この「If文」なら普通に英語を読むような感覚で処理が理解できると思いませんか?
また、条件や戻り値部分が多少長くなっても読みやすさはそこまで損なわれません

String s = If.correct(testSooooooooooooooooooooooooooooooooomething())
        .then("is trueeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee")
        .orElse("is falseeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee");
doSomething(s);

上記は当然コンパイルエラーになりますので、使用する場合は以下の3クラスを追加ください。(Java8以上のみ対応です)

public class If {
    public static BoolSaver correct(Boolean b) {
        return () -> b;
    }
}

@FunctionalInterface
public interface BoolSaver {
    Boolean get();

    default <A> Else<A> then(A a1) {
        return (A a2) -> get() ? a1 : a2;
    }
}

@FunctionalInterface
public interface Else<A> {
    A orElse(A a2);
}

サンプルとかgithubにアップしてるので興味があったら見てみてください。
https://github.com/mt-village/nagare


以下、追記

int[] a = {1,2};
int i = 3;
int result = If.correct(i < a.length).then(a[i]).orElse(-1);

みたいなパターンだとa[i]は実際には使われないのに例外(ArrayIndexOutOfBoundsException)が発生してしまうという問題を解決するために少し手を加えました。

int[] a = {1,2};
int i = 3;
// 通常の書き方
int a = If.correct(i < a.length).then(a.length).orElse(i);
// 戻り値の片方が例外を発生し得る場合の書き方
int b = If.correct(i < a.length)
    .then(() -> a[i])
    .orElse(() -> -1);

引数にSupplierを取れるようにしました。
安全面から考えるとSupplierの方だけにしたほうがいいと思いますが、そもそもの目的である可読性の向上という面から考えて、両方できるようにしておきました。

上記の書き方を有効にするには1クラス増えて、4クラスの追加が必要になります。

public class If {
    public static BoolSaver correct(Boolean b) {
        return () -> b;
    }
}

@FunctionalInterface
public interface BoolSaver {
    Boolean get();

    default <A> Else<A> then(A a1) {
        return (A a2) -> get() ?  a1 : a2;
    }

    default <A> ElseSaver<A> then(Supplier<A> a1) {
        return (Supplier<A> a2) -> get() ? a1.get() : a2.get();
    }
}

@FunctionalInterface
public interface Else<A> {
    A orElse(A a2);
}

@FunctionalInterface
public interface ElseSaver<A> {
    A orElse(Supplier<A> a2);
}

@saka1029 さん、ご指摘ありがとうございました!

26
24
9

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
26
24