Kotlin で、呼び出し側に処理して欲しい例外をどう扱うか。
- Web アプリケーションにおいて、例外的な状況を呼び出し側に処理して欲しい場合がある。
- サーバー内部で起こったどうしようもない例外的状況については、500 Internal Server Error でユーザーに返せば良いので、web 層で何かをする必要はない。
- 一方で、ユーザー入力の誤りだったり、DB 内の状態がクライアント側の想定している状態と違っていて衝突する場合など、丁寧にエラーメッセージを返す必要がある場合については、、アプリケーション層内部で検知されたそれらの例外的状況に対して、web 層で丁寧にメッセージを組み立てる必要がある。
- また、メソッドシグネチャのような形で、扱うべき例外的状況が明示されていると嬉しい。
- Kotlin ではチェック例外がないため、呼び出し側に送出された例外の処理を強制することができない。
- 組み込みの例外機構とは別の方法で、呼び出し側に例外的状況の処理を強制する方法を考える必要がある。
思いつく方法として、以下の 2 通りがある。
- (A)
Either
クラスなどを作って、レスポンスの型を成功時の型と失敗時の型の直和型っぽいもので表現する。 (ExceptionWithEither.kt の方法。) - (B) 例外的な状況ごとに例外クラスを生成するインターフェイスを定義し、その実装をメソッドパラメータで受け取るような設計にする。 (ExceptionWithMethodParameter.kt の方法。)
- 直和型が扱いやすいのであれば、(A) の方法が単純で良さそうに思える。 が、Kotlin の場合は直和型を表現するためにいちいち sealed class を定義する必要があり、扱いにくい。
- メソッドごとに sealed class を定義する必要がある。
- 下の層の sealed class で定義されている例外を表すクラスを、上の層の sealed class で再利用できない。 新たに定義しなおす必要がある。
- (B) の方法には上記の問題がない。
- (A) の方法では組み込みの例外機構を使用しないので、「呼び出し側が扱うべき例外的状況」 と 「本来は起こって欲しくない、どうしようもない例外的状況」 を明確に区別できる。 (前者はメソッドの返り値で扱い、後者を組み込みの例外機構で扱う。)
- (B) の方法では、呼び出し側が扱うべき例外的状況も、本来は起こって欲しくないどうしようもない例外的状況も、どちらも組み込みの例外機構で扱われる。
- 明確な区別をすることができなくなるが、呼び出し側がいい感じに扱うことでなんとかなる。
といったところを考えて、(B) の方法を使いたい。