Javaでラムダ式を使ってtry-catchの共通化を試みる
tl;dr
- 次のような処理のtry-catch節の共通化を試みる
class Main { public static void main(String[] args) { try { // 処理1 } catch (MyException e) { // 共通処理 } try { // 処理2 } catch (MyException e) { // 共通処理 } } }
- Exceptionをthrowできる Function Interfaceを定義する
- 実効する処理のFunctionとエラーハンドラ-を受け取る関数でtry-catchする
今回のソースコードはすべて以下にある
github.com
今回実装した方法より良い方法があれば教えてほしい。 あまりJavaの経験がないのでほぼ我流になってしまっている...。
共通化しない例
処理1の成功の可否に関わらず処理2を実行したときがあると思う。 おそらく以下のようなコードを書くことになる
class Main { public static void main(String[] args) { try { // 処理1 doSomething1(); } catch (MyException e) { // 共通処理 } try { // 処理2 doSomething2(); } catch (MyException e) { // 共通処理 } } public static void doSomething1() throws MyException { // 処理1 throw new MyException(); } public static void doSomething2() throws MyException { // 処理2 throw new MyException(); } }
処理2回ぐらいなら諦めも付くが、これが10回ぐらい続くと書くのも読むのも辛いので共通化したい。
はじめに最終形を記載しておく
public class Main { public static void main(String[] args) { // エラーを処理する関数 Consumer<MyException> errorHandler = (e) -> { // 共通処理 }; tryCatch(Main::doSomething1, errorHandler); tryCatch(Main::doSomething2, errorHandler); } public static void tryCatch(RunnableThrowableMyException func, Consumer<MyException> errorHandler) { try { func.run(); } catch (MyException e) { errorHandler.accept(e); } } public static void doSomething1() throws MyException { // 処理1 throw new MyException(); } public static void doSomething2() throws MyException { // 処理2 throw new MyException(); } }
MyExceptionの定義
今回使う自作のExceptionクラスを定義しておく。 単純にExceptionクラスを継承する自作のExceptionクラスで、とくに変わったことはしてないと思う。
package exceptions; public class MyException extends Exception { public MyException() { } public MyException(Exception e) { super(e); } }
今回の処理1, 2では問題が起きたときこのMyException
をthrowする
Exceptionをthrowできる関数Interfaceを定義する
もう一度共通化前にソースを見る。
class Main { public static void main(String[] args) { try { // 処理1 } catch (MyException e) { // 共通処理 } try { // 処理2 } catch (MyException e) { // 共通処理 } } }
まず考えるのは処理1, 2とエラーハンドリングの共通処理をラムダ式にして、 一つの関数で処理するような以下のようなコードだと思う。
public class Main { public static <R> void tryCatch(Runnable func, Function<MyException, R> errorHandler) { try { func.run(); } catch (MyException e) { errorHandler.apply(e); } } public static void main(String[] args) { // エラーを処理する関数 Function<MyException, Void> errorHandler = (e) -> { // 共通処理 return null; }; tryCatch(Main::doSomething1, errorHandler); tryCatch(Main::doSomething2, errorHandler); } }
試しにこのコードを書いてみると RunnableはMyExceptionをthrowしないので駄目っぽい。
そこでMyExceptionをthrowできるRunnable Interfaceを定義する
以下のRunnable.javaを参考に github.com
MyExceptionをthrowするRunnable Interfaceを定義する
@FunctionalInterface public interface RunnableThrowableMyException { void run() throws MyException; }
処理とエラーハンドラーを受け取って処理する
RunnableThrowableMyException
を実装したので、 処理とエラーハンドラーを受け取ってtry-catchするメソッドを実装する
public class Main { public static void tryCatch(RunnableThrowableMyException func, Consumer<MyException> errorHandler) { try { func.run(); // 処理を実行。MyExceptionがthrowされる } catch (MyException e) { errorHandler.accept(e); // 受け取ったエラーハンドラーで処理する } } public static void main(String[] args) { // エラーを処理する関数 Consumer<MyException> errorHandler = (e) -> { // 共通処理 }; tryCatch(Main::doSomething1, errorHandler); tryCatch(Main::doSomething2, errorHandler); } public static void doSomething1() throws MyException { // 処理1 throw new MyException(); } public static void doSomething2() throws MyException { // 処理2 throw new MyException(); } }
もう少し推し進めて、処理をListで受け取るようにする
public class Main { public static void tryCatch(RunnableThrowableMyException func, Consumer<MyException> errorHandler) { try { func.run(); // 処理を実行。MyExceptionがthrowされる } catch (MyException e) { errorHandler.accept(e); // 受け取ったエラーハンドラーで処理する } } public static void tryCatch(List<RunnableThrowableMyException> functions, Consumer<MyException> errorHandler) { // 処理をリストで受け取って処理する functions.forEach(func -> tryCatch(func, errorHandler)); } public static void main(String[] args) { // エラーを処理する関数 Consumer<MyException> errorHandler = (e) -> { // 共通処理 }; List<RunnableThrowableMyException> functions = new ArrayList<RunnableThrowableMyException>(){ { add(Main::doSomething1); add(Main::doSomething2); } }; TryCatch.tryCatch(functions, errorHandler); } public static void doSomething1() throws MyException { // 処理1 throw new MyException(); } public static void doSomething2() throws MyException { // 処理2 throw new MyException(); } }
今回のソースコードはすべて以下にある
github.com