[Javaの小枝] static import を使って制御構文もどきを作る

# 概要
static import を利用して制御構文もどきを作る。

# はじめに
例えば web API 呼びだしにおいて「あるロジックを実行し、正常終了なら先へ進むが、例外が発生してもある制限回数までは例外を無視して同じロジックの再実行を試みる。制限回数に到達したら、例外をスローする」などという比較的複雑なフローが繰り返しコードに現われてしまうことがないだろうか。

このような場合、static import とラムダ式の組合せで制御構文もどきのコードを書くことにより、上記のような複雑なフローをまるで単純なループのような見掛けで記述できる。(ラムダ式を使わなくても見掛けが少しやぼったくなるだけなので最後まで読んで欲しい)

# 実装
まず準備として下記のようなクラスを作る。

```Java:Utils.java

public class Utils {

	public static class Retry {

		public static class RetryableException extends Exception {}
		@FunctionalInterface 
		public static interface RetryableRoutine {
				public void run() throws RetryableException;
		}
		
		private int limit = 1;
		public Retry(int limit_) {limit = limit_;}

		public void perform(RetryableRoutine routine) throws RetryableException {
			for (int i = 0; i < limit - 1; i ++) {
				try {
						routine.run(); return;
				} catch (RetryableException e) {}
			}
			try {
				routine.run(); return;
			} catch (RetryableException e) {throw e;}
		}
	}
	
	public static Retry retry(int num) {return new Retry(num);}
	
}

```

そしてこのクラスの中の Utils.retry を static import する。すると例えば下記のようにエラーが頻発するロジックのリトライ処理を簡潔に記述することができるのだ。

```Java:Sample.java
import Utils.Retry.RetryableException;
import static Utils.retry;

public class Sample {

    private static int counter = 0;

    public static void main(String[] args) throws RetryableException {

        retry(10).perform(   // 再実行を10回まで試みる
            () -> {

                // 以下はweb API のように例外が頻発するロジックの例
                counter ++;
                if (counter < 8) throw new RetryableException(); // 8回連続で例外を投げ
                System.out.println("hoge"); // 9回目で最後まで走る

            }
        );
    }

}
```
もちろん Java7 までの世界でも perform() の中身の書き方が少しかわるだけで基本的な考え方は応用できる。ここでは retry 中に投げられた例外を意図的に無視したコードにしたが、説明のために理解しやすさを優先しただけで、例外ハンドリングを付け加えることはもちろん簡単にできる。各自で工夫してみて欲しい。

この考え方を進めていくと、関数型言語におけるポピュラーな構文「パターンマッチ」に似たような書き方まで可能になってしまう。

まず、下記のようなクラスを準備する。

```Java:Utils.java
public class Utils {

    public static class Matcher<I, O> {
        private final I input;
        private final Supplier<O> defaultOutput;
        public Matcher(I input_, Supplier<O> defaultOutput_) {input = input_; defaultOutput = defaultOutput_;}

        @SafeVarargs
        public final O perform(Case<I, O>... cases) {
            for (Case<I, O> c : cases) if (c.condition.apply(input)) return c.supplier.get();
            return defaultOutput.get();
        }
    }

    public static class Case<I, O> {
        private final Function<I, Boolean> condition;
        private final Supplier<O> supplier;
        public Case(Function<I, Boolean> condition_, Supplier<O> supplier_) {condition = condition_; supplier = supplier_;}
    }

    public static class RegularExpressionHolder {
        private final String expression;
        private final Pattern pattern;
        public RegularExpressionHolder(String expression_) {expression = expression_; pattern = Pattern.compile(expression);}
        public boolean matches(String str) {return pattern.matcher(str).matches();}
    }

    public static <I, O> Matcher<I, O> matcheOrElse(I input, O defaultOutput) {return new Matcher<I, O>(input, () -> defaultOutput);}
    public static <I, O> Matcher<I, O> matcheOrElse(I input, Supplier<O> defaultOutput) {return new Matcher<I, O>(input, defaultOutput);}

    public static <I, O> Case<I, O> inCaseOfReturn(I pattern, O output) {return new Case<I, O>(i -> pattern.equals(i), () -> output);}
    public static <I, O> Case<I, O> inCaseOfReturn(Function<I, Boolean> condition, O output) {return new Case<I, O>(condition, () -> output);}
    public static <I, O> Case<I, O> inCaseOfReturn(I pattern, Supplier<O> output) {return new Case<I, O>(i -> pattern.equals(i), output);}
    public static <I, O> Case<I, O> inCaseOfReturn(Function<I, Boolean> condition, Supplier<O> output) {return new Case<I, O>(condition, output);}
    public static <O> Case<String, O> inCaseOfReturn(RegularExpressionHolder regExp, Supplier<O> output) {return new Case<String, O>(i -> regExp.matches(i), output);}
    public static <O> Case<String, O> inCaseOfReturn(RegularExpressionHolder regExp, O output) {return new Case<String, O>(i -> regExp.matches(i), () -> output);}

    public static RegularExpressionHolder regExp(String pattern) {return new RegularExpressionHolder(pattern);}
}
```

すると下記のように、まるでJavaでも関数型言語でのパターンマッチが出来るかのようなコードが可能になる。まぁ本物の関数型言語のパターンマッチにはどこまで行っても同じにはならないのだが…

```Java:Sample.java
import static Utils.matcheOrElse;
import static Utils.inCaseOfReturn;
import static Utils.regExp;

public class Sample {

    public static void main(String[] args) {

        String input = "aaa";

        String result = matcheOrElse(input, "bang!").perform(                    // input が下記のパターン全てにマッチしなければ "bang!" を返す

                inCaseOfReturn("hoge", () -> input + "_size = " + input.length()),    // inputが即値 "hoge" だったら右のロジックを実行して結果を返す 
                inCaseOfReturn(inp -> "baa".compareTo(inp) > 0 , input),        // 左の条件式に合致したら右の値を返す
                inCaseOfReturn(regExp("h*ge"), "matcher successfully finished")            // 左の正規表現に合致したら右の値を返す

        );

        System.out.println(result);
    }

}
```

便利ではある一方、やりすぎは危険かもしれない。例えば初見の人に対する可読性を下げるなどがありうる。ある程度のところでやめておこう。