私はJavaでしばらくコーディングしています。しかし、例外をいつスローすべきか、いつキャッチすべきかがわからない場合があります。多くのメソッドがあります。階層は次のようなものです。
Method A will call Method B and Method B will call some Method C and Method C will call Method D and Method E.
したがって、現在私がしていることは、すべてのメソッドで例外をスローし、メソッドAでそれをキャッチし、エラーとしてログに記録することです。
しかし、これが正しい方法かどうかはわかりませんか?または、すべてのメソッドで例外のキャッチを開始する必要があります。だから、この混乱が私の中で始まったのです。いつ例外をキャッチすべきか、いつ例外をスローすべきか。私はそれがばかげた質問であることを知っていますが、どういうわけかこの主要な概念を理解するのに苦労しています。
誰かがWhen to catch the Exception vs When to throw the Exceptions
の詳細な例を教えてくれて、これで私の概念が明確になりますか?そして、私の場合、例外をスローし続け、メインの呼び出しメソッドAでキャッチする必要がありますか?
何をすべきかを知っているメソッドにいるときは、例外をキャッチする必要があります。
たとえば、今のところ実際にどのように機能するかを忘れてください。ファイルを開いて読み込むためのライブラリを書いているとしましょう。
クラスがあります。
public class FileInputStream extends InputStream {
public FileInputStream(String filename) { }
}
ここで、ファイルが存在しないとしましょう。あなたは何をするべきか?答えを考えるのに苦労しているのは、それがないからです... FileInputStream
はその問題について何をすべきかを知りません。だから、それをチェーンに投げる、すなわち:
public class FileInputStream extends InputStream {
public FileInputStream(String filename) throws FileNotFoundException { }
}
次に、誰かがあなたのライブラリを使用していると言いましょう。次のようなコードがあります。
public class Main {
public static void main(String... args) {
String filename = "foo.txt";
try {
FileInputStream fs = new FileInputStream(filename);
// The rest of the code
} catch (FileNotFoundException e) {
System.err.println("Unable to find input file: " + filename);
System.err.println("Terminating...");
System.exit(3);
}
}
}
ここで、プログラマは何をすべきかを知っているので、例外をキャッチして処理します。
関数でエラー、つまりエラーが発生した場合、例外をスローする必要があります。
機能は作業の単位であり、障害はエラーまたは機能への影響に基づいて判断する必要があります。関数内でfの場合、fが呼び出し先のpreconditionsのいずれかを満たすことができない場合にのみ、エラーはエラーです。 f自身事後条件、または任意の再確立不変式f維持の責任を共有します。
エラーには次の3種類があります。
他の条件はnotエラーであり、エラーとして報告されるべきではありません。
関数がそれ自体で処理できないエラーを検出し、通常の操作または意図した操作のいずれかの形式で続行できないようにするエラーを検出した場合は、エラーを報告します。
エラーの処理、翻訳、またはmainやスレッドメインラインなどのエラーポリシーで定義された境界の実施に十分な知識がある場所でエラーを処理します。
1つまたは2つの実稼働環境でベーコンを保存したパターンを共有します。
動機
私の目的は、sev1サポートチケットを解決しようと真夜中にいる貧しい人(たぶん私)が 'コードを過度に混乱させることなく、IDなどのデータを備えた完全なエラーが発生します。
メソッド
これを実現するために、チェック済み例外をすべてキャッチし、それらを未チェック例外として再スローします。次に、各アーキテクチャレイヤーの境界でグローバルキャッチを使用します(通常は抽象化または挿入されるため、一度しか記述されません)。エラースタックに追加のコンテキストを追加したり、ログに記録して無視したり、追加のコンテキストを保持するために変数を使用してカスタムチェック例外を発生させたりすることができます。余談ですが、「ダブルロギング」の発生を停止するために、最上位層でエラーを記録するだけです(例:cronジョブ、ajaxのスプリングコントローラー)
throw new RuntimeException(checked,"Could not retrieve contact " + id);
このアプローチでは、データベース関連の例外に対して「スロー」を宣言する必要があるため、GUIまたはビジネス層のメソッドシグネチャが乱雑になりません。
これが実際にどのように機能するかの例:
私のコードの仕事は、多くの保険契約を更新する自動化されたプロセスだとしましょう。このアーキテクチャは、1つのポリシーの更新を手動でトリガーするGUIをサポートしています。また、これらのポリシーの1つについて、評価領域の郵便番号がDBで破損しているとしましょう。
私が達成したいエラーログのタイプの例は次のようになります。
ログメッセージ:ポリシー1234のエラーによる手動介入のフラグ:
スタックトレースから:エラー更新ポリシー1234。トランザクションのロールバック...このキャッチは、保存エラーやレターの生成などのエラーもカバーします。
スタックトレースから:原因:エラーレーティングポリシー1234 ...このキャッチは、他の多くのオブジェクトの取得エラー、およびNPEなどのアルゴリズムエラーをピックアップします...
スタックトレースから:原因:評価エリア73932の取得エラー...
スタックトレースから:原因:JPA:フィールド 'postcode'に予期しないnull
一般的には、何か役に立つことができるレベルでキャッチします。たとえば、ユーザーがデータベースに接続しようとしていますが、方法Dで失敗します。
どのように処理しますか?おそらく、「申し訳ありませんが、SERVER/DBに接続できません」などのダイアログを表示します。メソッドA、B、またはCがこのSERVER/DB情報を作成し(たとえば、設定ファイルを読み取るか、ユーザー入力を要求することにより)、接続を試みましたか?それはおそらく例外を処理するメソッドです。または、それを処理するメソッドから少なくとも1つ離れている必要があります。
実際にはアプリケーションによって異なるため、これは非常に一般的なアドバイスにすぎません。私の経験のほとんどはSwing /デスクトップアプリに関するものであり、通常、プログラムロジックを実行しているクラス(「コントローラー」など)とダイアログボックスを配置しているユーザー(「ビュー」など)に基づいて、感覚をつかむことができます。通常、「コントローラー」は例外をキャッチして、何かをしようとします。
Webアプリでは、これは異なる場合があります。
いくつかの非常に骨格的なコード、ほとんどのクラスは存在せず、DBのURLが意味をなすかどうかはわかりませんが、アイデアは得られます。漠然と揺れる...
/* gets called by an actionListener when user clicks a menu etc... */
public URL openTheDB() {
URL urlForTheDB = MyCoolDialogUtils.getMeAURL(URL somePreviousOneToFillInTheStart);
try {
verifyDBExists(urlForTheDB);
// this may call a bunch of deep nested calls that all can throw exceptions
// let them trickle up to here
// if it succeeded, return the URL
return urlForTheDB;
}
catch (NoDBExeption ndbe) {
String message = "Sorry, the DB does not exist at " + URL;
boolean tryAgain = MyCoolDialogUtils.error(message);
if (tryAgain)
return openTheDB();
else
return null; // user said cancel...
}
catch (IOException joe) {
// maybe the network is down, aliens have landed
// create a reasonable message and show a dialog
}
}
例外は可能な限り低いレベルで処理する必要があります。メソッドが例外を適切に処理できない場合は、スローする必要があります。
通常、メソッドの失敗を呼び出し元に通知する場合、例外をスローします。
例:無効なユーザー入力、データベースの問題、ネットワークの停止、ファイルの不在
他の人が言ったように、一般的なルールとして、実際に処理できる場合は例外をキャッチする必要があります。そうでない場合は例外をスローします。
たとえば、保存ファイルから接続しているプレーヤーに関する情報を読み取るコードを作成していて、I/Oメソッドの1つがIOException
をスローする場合、その例外と、 load
メソッドは、その例外をキャッチし、それに応じて処理します(プレーヤーを切断する、クライアントに応答を送信するなど)。 load
メソッドで例外を処理したくない理由は、このメソッドでは例外を有意義に処理できないため、例外を処理できるように呼び出し元に委任するためです。