Java8で複数の処理を確実に実行するイディオム

たとえばこんな感じで複数の処理があるとしますね。

public static void proc1(){
    System.out.println("proc1");
}
public static void proc2(){
    System.out.println("proc2");
}
public static void proc3(){
    System.out.println("proc3");
}


この、proc1とproc2とproc3を、すべて実行する必要があるとします。どれかで例外が出ても残りは必ず実行できるように、と。
そうすると、それぞれtry〜catchで囲んで処理をする必要があるのですが、それを便利に記述できるイディオムがあります。


こんな感じ

try(Closeable c = () -> proc1();
    Closeable c2 = () -> proc2();
    Closeable c3 = () -> proc3();
){
}

try-with-resources構文のtry句で、Closeable型の変数にLambdaを割り当てつつ所望の処理を書くという形です。


ちゃんと実行されますね。

proc3
proc2
proc1

書いたのと逆順だけど。


このとき、もしproc2で例外が出るとしますね。

public static void proc2(){
    throw new RuntimeException("例外だよー");
}


それでもproc1とproc3はちゃんと実行されて、その上で例外が投げられます。

proc3
proc1
Exception in thread "main" java.lang.RuntimeException: 例外だよー


これのいいところは、複数の例外を自動でたばねてくれるところです。
proc1でも例外が出るとします。

public static void proc1(){
    throw new RuntimeException("例外だよー");
}


そうすると、投げられる例外の中でこんな感じでSuppressedに他の例外がまとめられます。

proc3
Exception in thread "main" java.lang.RuntimeException: 例外だよー
 at app.TWR.proc2(TWR.java:20)
 at app.TWR.lambda$1(TWR.java:28)
 at app.TWR$$Lambda$2/1523554304.close(Unknown Source)
 at app.TWR.main(TWR.java:31)
 Suppressed: java.lang.RuntimeException: これも例外だよー


ちゃんと例外を捕まえて、すべての例外を処理する場合はこんな感じになりますね。

try(Closeable c = () -> proc1();
    Closeable c2 = () -> proc2();
    Closeable c3 = () -> proc3();
){
}catch(Exception e){
    Stream.concat(
            Stream.of(e), 
            Arrays.stream(e.getSuppressed()))
        .forEach(ex -> System.out.println(ex.getMessage()));
}


こんな感じになります。

proc3
例外だよー
これも例外だよー


これで、確実に処理したいコードが手軽に書けますね!

注意事項

書いたのと逆順に実行されることには注意が必要です。
あと、本番コードでこんな書き方しないようにしましょう。

リーダブルコード ―より良いコードを書くためのシンプルで実践的なテクニック (Theory in practice)

リーダブルコード ―より良いコードを書くためのシンプルで実践的なテクニック (Theory in practice)