Javaアプリケーションでランタイム例外をスローする


11

私は請負業者として、テクニカルリードの役割でクライアント用のエンタープライズJavaアプリケーションを設計しています。このアプリケーションはエンドユーザーによって使用され、退社時にアプリケーションをサポートするサポートチームが存在します。

私が一緒に作業している他のテクニカルリードは、例外処理によってコードがダーティになるという印象を受けています。システムはチェックされていない例外を処理する必要がないように、チェックされた例外をサービス層からのみスローし、残りのコードは他のすべての層からランタイム例外をスローする必要があります。

ビジネスアプリケーションで未チェックの例外をスローする必要はありますか?

ランタイム例外に関する過去の私の経験から:

1)チェックされていない例外は、Javadocにも表示されないため、コードを予測不能にします。

2)ビジネスアプリケーションで未チェックの例外をスローするのは意味がありません。スローしてユーザーの顔にまっすぐに進むと、どのようにユーザーに説明するのですか?500 -Internal Error. Contact Administratorエンドユーザーにとっても、アプリケーションを管理するサポートチームにとっても何の意味もないことを示すWebアプリケーションを十分に見てきました。

3)ランタイム例外をスローすると、例外をスローするクラスのユーザーはソースコードを調べてデバッグし、例外がスローされた理由を確認することが強制されます。これは、実行時例外のJavadocが偶然見事に文書化されている場合にのみ回避できますが、これは決して事実ではありません。


サービス層とは何ですか?レイヤーはユーザーから最も遠いですか、それともユーザーに最も近いですか?
マノジR

この質問がSOに合わないのはなぜですか?
ビャルケフロイントハンセン

@マノジR:どちらでもない。サービス層またはサービス層は、ビジネスルールとロジックが実行される場所であり、DBテクノロジーとクライアントプレゼンテーションの両方の詳細から分離されています。
マイケルボルグワード

6
未チェックの例外[...]は、Javadocにも表示されません。=> @throwsタグで文書化すると表示されますが、これは良い習慣です。
-assylias

4
@SureshKoya Effective Java、Item 62:各メソッドによってスローされるすべての例外を文書化します。抽出:Javadoc @throwsタグを使用して、メソッドがスローできる各未チェック例外を文書化しますが、throwsキーワードを使用して、メソッド宣言に未チェック例外を含めないでください。
アッシリア

回答:


12

チェックされた例外は、言語設計の失敗した実験です。それらは、あなたに非常に漏れやすい抽象化と汚いコードを強制します。それらは可能な限り避けるべきです。

あなたのポイントに関して:

1)チェックされた例外はコードを汚くします

2)チェックされた例外はユーザーにどのように表示されるのですか?チェック済み例外と未チェック例外の唯一の本当の違いは技術的なものであり、ソースコードのみに影響します。

3)スタックトレースを聞いたことがありますか?例外がチェックされているかどうかに関係なく、例外がスローされた場所を正確に示します。実際、チェックされた例外はラップされることが多いため、デバッグでは悪化する傾向があります。これは、長くていスタックトレースにつながるか、ラップが間違っていたために完全に失われることさえあります。

例外には2つの種類があります。「通常」発生し、通常は発生した場所の非常に近くで処理される例外と、非常に例外的で非常に高い層で一般的に処理できる例外です(現在のアクションを中止して記録するだけです/エラーを表示)。

チェック例外は、例外が定義された時点で、この区別を言語構文に入れようとする試みでした。これに関する問題は

  • 例外は実際には呼び出し元に依存し、例外をスローするコードではありません
  • 例外のセマンティックな意味とは完全に直交していますが、クラス階層に結び付けると、2つを混在させる必要があります
  • 例外の全体的なポイントは、エラーを静かに失うことなく、または中間レベルでコードを汚染することなく、それらをキャッチするレベルを決定できることです。チェック例外は、2番目の利点を失います。

1.さて、未チェックの例外の場合、呼び出しが未チェックになる可能性があるかどうかさえわかりません。2.「例外のセマンティックな意味とは完全に直交しているが、クラス階層に結び付けると2つを混在させなければならない」と言ったとき、クラス階層ではなく呼び出し階層を意味すると思います。
randominstanceOfLivingThing

2
@Suresh Koya:いいえ、クラス階層を意味しました。宣言SQLException extends ExceptionSQLException、エラーがDBアクセスに関係するため自動的に例外をチェックすることを意味するという意味です。また、Javadocコメントで未チェックの例外を適切に文書化できます。他のすべての言語でうまく機能します。
マイケルボルグワード

1
@MichaelBorgwardt「チェックされた例外は言語設計の失敗した実験です」、そうでない場合はあなたの個人的な意見ですか。それについてもっと読みたいです。本物のリンクは大いに役立つでしょう。
Vipin

2
@Vipin:それは私の個人的な意見ですが、他の多くの人が共有しているもの、たとえばBruce Eckelmindview.net/Etc/Discussions/CheckedExceptions-そして、Javaが導入されてから20年以内に他の言語がチェック例外を採用していないという事実が語っています自分自身のために...
マイケルBorgwardt

2

私の見解では、どのタイプの例外がスローされるかは、コードの実行内容に依存します。

頻繁に発生する可能性があると予想されるときにチェック例外をスローし(ユーザーが危険なデータを入力するなど)、呼び出しコードがその状況を処理することを期待しています。

呼び出しコードが処理することを期待しないまれな状況がある場合、未チェック/実行時例外(チェックほど頻繁ではない)をスローします。例は、私が決して発生するとは思わない奇妙なメモリ関連のエラーのようなものかもしれません。チェックされていないのは、アプリケーションをダウンさせると予想される種類のエラーです。

何らかのレベルのサポートがなければ、ユーザーの前に例外は表示されません。たとえ「このエラーダンプを電子メールにカットアンドペーストしてください」だけの場合でも。エラーがあると言われることほどユーザーを悩ますことはありませんが、修正を開始するために実行できる詳細やアクションは提供されません。

私の推測では、あなたが言及する哲学は、2つのソースのうちの1つから来ていると思います:

  • 仕事を避けようとする怠zyなプログラマー。
  • または、逆の方法でコードをサポートする必要がある開発者。すなわち、過剰エラー処理。大量の例外処理を含む種類のコードは、そのほとんどが何もしないか、さらに悪いことに、フロー制御やその他の不正な目的に使用されます。

何かが頻繁に発生する場合、それは例外ではありません。しかし、エラーがスローされるべきではないことに同意しました。
マノジR

あなたの哲学に同意します。ユーザー向けに開発しているアプリケーションでは、ランタイム例外をスローする必要がありますか?
randominstanceOfLivingThing

また、ランタイム例外がスローされた場合でも、@ assyliasが指す規則を好みます。
randominstanceOfLivingThing

1

未チェックのランタイム例外が必要ない場合、なぜjavaを使用しているのですか?ほとんどすべての場所でランタイム例外が発生する可能性があります-特にNullPointerException、ArithmeticException、ArrayIndexOutOfBounds例外など

また、たとえばSAP NetWeaverインストールなどのJ2EEシステムのログファイルを一度読むと、そのような例外が文字通り常に発生していることがわかります。


Java言語仕様から:「ランタイム例外クラス(RuntimeExceptionとそのサブクラス)は、Javaプログラミング言語の設計者の判断では、そのような例外を宣言しなければ、正確性を確立するのにあまり役立たないため、コンパイル時のチェックから除外されますJavaプログラミング言語の操作と構成の多くは、実行時例外を引き起こす可能性があります。」
randominstanceOfLivingThing

1
あなたが話しているランタイム例外は、Javaランタイムシステムには特定の呼び出しをしようとしている理由の手がかりがないため、Javaから発生する例外です。例:nullのメソッドを呼び出している場合、NullPointerExceptionが発生します。その場合、Javaランタイムシステムにはプログラマーの愚かしい言葉がなく、NullPointerExceptionで呼び出し側を平手打ちします。悲しい部分は、呼び出し元があなたにNetweaver製品を販売したことであり、ユーザーとしてのあなたは今や貧弱なプログラミングの犠牲者です。すべての境界テストを実行しなかったため、テスターも同様に非難する必要があります。
randominstanceOfLivingThing

1

留意すべき例外処理のルールがいくつかあります。ただし、最初に、例外はコードによって公開されるインターフェイスの一部であることを覚えておく必要があります。それらを文書化します。もちろん、インターフェイスがパブリックな場合は特に重要ですが、プライベートインターフェイスでも非常に良い考えです。

例外は、コードが例外を適切に処理できる時点でのみ処理する必要があります。最悪の処理オプションは、それらについて何もしないことです。これは、それが正確に正しいオプションである場合にのみ実行する必要があります。(コードにこのような状況がある場合は、空のボディを心配しないように、その効果へのコメントを含めます。)

2番目の最悪のオプションは、原因としてアタッチされた元のものなしで無関係な例外をスローすることです。ここでの問題は、問題の診断を可能にする元の例外内の情報が失われることです。あなたは誰も何もできないものを作成しています(「機能しない」と文句を言う以外に、私たちは皆、これらのバグレポートを嫌う方法を知っています)。

はるかに良いのは、例外をログに記録することです。これにより、誰かが問題が何であるかを見つけて修正できますが、例外は、外部接続を介して失われたり報告された場合にのみログに記録する必要があります。それは、より頻繁にログを記録することが大きな問題であるためではなく、過剰なログ記録は、ログをより多くの情報を含むことなくより多くのスペースを消費することを意味するためです。あなたは例外をログに記録したら、報告することができPRECISをショートバージョンが一致させることができるように、そのレポート-または他の相関識別子-限り、あなたはまた、世代の時間を含めるよう良心(とユーザー/クライアントに必要に応じて詳細を確認してください)。

もちろん、最良のオプションは、例外を完全に処理し、エラー状況を完全に処理することです。あなたがこれを行うことができるなら、必ずそれをしてください!例外をログに記録する必要がなくなることを意味する場合もあります。

例外を処理する1つの方法は、問題のより高いレベルの説明を提供する別の例外をスローすることです(例えば、「failed to initialize」ではなく「index out of bounds」)。これは、例外の原因に関する情報を失わない限り、良いパターンです。詳細な例外を使用してcause、上位レベルの例外を初期化する、詳細を記録します(上記を参照)。低レベルの例外クラスが接続のもう一方の端に存在するという保証がないため、IPCコールなどのプロセス間境界を越えようとする場合、ロギングが最も適切です。内部の境界を越える場合は、原因として維持することが最も適切です。

あなたが見る別のパターンはキャッチアンドリリースです:

try {
    // ...
} catch (FooException e) {
    throw e;
}

これは、他の句から型制約を取得しない限り、アンチパターンです。catch例外を単独で通過させることはできません。それはJavaのい機能にすぎません。

チェック例外と未チェック例外の違いは、メソッドの境界を越えるチェック例外を宣言する必要があるという事実以外にはありません@throwsコードによって意図的にスローされていることがわかっている場合は、未チェックの例外を(javadocコメントとともに)文書化することをお勧めします。java.lang.Error(JVM実装を書いている場合を除き)意図的にthrow またはそのサブクラスを投げないでください。

意見:予期しないエラーの場合は、常にコードのバグを表します。チェックされた例外はこの脅威を管理する方法であり、開発者がエラーケースの処理のトラブルを回避する方法として未チェックの例外を意図的に使用している場合は、ある程度クリーンアップする必要がある多くの技術的負債を積み上げています堅牢なコードが必要な場合。ずさんなエラー処理は専門的ではありません(また、エラー処理を確認することは、プログラマーがどれほど優れているかを判断するための良い方法です)。


0

アプリケーションが回復できる場合にのみ、チェック済み例外をスローする必要があると思います。そのため、ライブラリのユーザーはこれらの例外をキャッチし、回復に必要なことを実行する必要があります。それ以外はチェックを外す必要があります。

例として、

アプリがファイルまたはデータベースからデータをロードする場合。次に、..

try {
  File data = new File(...);
  // parse file here
} catch (Exception ex) {
  throw new MissingDataFileException("data file not found");
}

呼び出し側は、常にチェックされたMissingDataFileExceptionをキャッチしてから、データベースからデータをロードしようとします。

try {
  Connection con = DriverManager.getConnection( host, username, password );
  // query data here
} catch (Exception ex) {
  throw new RuntimeException("better call Saul!");
}

1
サウルは3年前に自動車事故で亡くなりました。不合格!;-)
ドナルドフェローズ
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.