私は両方の状況にありました:
- カスタム例外の作成が多すぎる
- 一般的な例外クラスの使用が多すぎます
どちらの場合も、プロジェクトは問題なく開始されましたが、すぐに維持 (およびリファクタリング) のオーバーヘッドになりました。
独自の例外クラスの作成に関するベスト プラクティスは何ですか?
Java スペシャリストはExceptions in Javaに関する投稿を書き、その中で例外を作成するためのいくつかの「ベスト プラクティス」をリストし、以下に要約します。
独自の例外を記述しない (すでに Java API の一部になっている便利な例外がたくさんあります)
有用な例外を作成する (独自の例外を作成する必要がある場合は、発生した問題に関する有用な情報を提供するようにしてください)
私の会社の開発者がしたことをしないでください。java.lang.IllegalArgumentException に相当する [sic] InvalidArgumentException を誰かが作成し、現在では (文字通り) 何百ものクラスで使用しています。どちらも、メソッドに不正または不適切な引数が渡されたことを示します。無駄話…
Joshua Bloch は、Effective Java Programming Language Guide [ベスト プラクティスに関する最初の手段のバイブル] の第 8 章 例外でこれについて説明 しています。ここに彼の言葉の一部があります。
既存の例外を再利用すると、いくつかの利点があります。これらの中で最も重要なのは、API が、プログラマーが既に慣れ親しんでいる確立された規則に一致するため、API の学習と使用が容易になることです[ブロッホではなく私が強調する]。2 番目に近いのは、API を使用するプログラムは、見慣れない例外で散らかっていないため、読みやすくなっているということです。最後に、例外クラスが少ないということは、メモリ フットプリントが小さくなり、クラスのロードにかかる時間が短縮されることを意味します。
最も一般的に再利用される例外は IllegalArgumentException です。これは通常、呼び出し元が値が不適切な引数を渡したときにスローされる例外です。たとえば、あるアクションを繰り返す回数を表すパラメーターに呼び出し元が負の数を渡した場合、これは例外としてスローされます。
とはいえ、例外自体をスローするべきではありません。Javaには、ほとんどの状況をカバーし、原因を修正できるように十分に発生した例外を説明する、適切に選択され、多様で、適切にターゲットを絞った一連の組み込み例外があります。
将来あなたのコードを保守しなければならないプログラマーに親切にしましょう。
私の経験則では、スローされた例外の種類に応じて、クライアント (呼び出し元) が別のことを合理的に実行したい場合、追加の例外の種類が保証されます。ただし、多くの場合、追加の例外タイプは必要ありません。たとえば、呼び出し元が次のようなコードを書いている場合
try {
doIt();
} catch (ExceptionType1 ex1) {
// do something useful
} catch (ExceptionType2 ex2) {
// do the exact same useful thing that was done in the block above
}
明らかに、追加の例外タイプは必要ありません。呼び出されるコードが新しい例外タイプの作成に熱心すぎるため、このようなコードをよく見かけます (または、書かざるを得ません)。
発生したエラーの種類を説明する名前を持つ例外が見つからない場合は、自分で作成します。
それが私の経験則です。
基本的に、各ジョブには独自の例外があります。例外をキャッチする場合、通常はオブジェクトで行うように、異なるインスタンスを区別しないため、異なるサブタイプが必要になります。あまりにも多くのカスタム例外を使用することは、ほとんど発生しないケースです。
1 つのアドバイスは、必要に応じて例外を作成することです。1 つの例外タイプが別のタイプと重複していることが明らかになった場合は、2 つをマージしてコードをリファクタリングします。もちろん、最初から例外を構造化することを考えれば、それは役に立ちます。ただし、一般的には、既存の状況固有の例外と 1 対 1 で対応していないすべてのケースに対してカスタム例外を使用します。
一方、NullPointerException
s とIndexOutofBoundsException
s は実際には適切な場合が多いです。ただし、これらは (ロギングを除いて) キャッチしないでください。これらはプログラミング エラーであり、スローした後、プログラムが未定義の状態になることを意味します。
独自の例外を作成するとき:
すべての例外は、Throwable クラスの子でなければなりません
ハンドルまたは宣言ルールによって自動的に適用されるチェック例外を作成する場合は、例外クラスを拡張する必要があります。
実行時例外を作成する場合は、Runtime Exception Class を拡張する必要があります。
私自身の経験則:
スローするものが無関係であり、余分な時間を費やす理由がない単体テストを除いて、例外をスローすることはありません。
カスタム ビジネス ロジックで発生するエラーに対して、独自のカスタム例外タイプを作成します。この例外タイプは、他の例外を再キャストするために可能な限り使用されます。ただし、クライアントが実際に何が発生したかを可視化することが理にかなっている場合は除きます。
独自の例外を作成しないでください。すでにある以下のものを使用してください。
IllegalStateException
UnsupportedOperationException
IllegalArgumentException
NoSuchElementException
NullPointerException
未チェックの例外をスローします。
public void validate(MyObject myObjectInstance) {
if (!myObjectList.contains(myObjectInstance))
throw new NoSuchElementException("object not present in list");
}