チェックされた例外に対するケース


456

何年もの間、私は次の質問に対して適切な答えを得ることができませんでした:一部の開発者はなぜチェックされた例外に反対するのですか?私は数多くの会話をしたり、ブログで物事を読んだり、ブルース・エッケルが言わなければならなかったことを読んだりしました(私が最初に見た人が彼らに反対して声を上げました)。

私は現在、いくつかの新しいコードを書いており、例外の処理方法に細心の注意を払っています。「チェックされた例外は好きではない」群衆の視点を確認しようとしていますが、それでも確認できません。

私が行ったすべての会話は、同じ質問に答えられずに終わります...設定させてください:

一般的に(Javaの設計方法から)、

  • Error 決して捕まってはならないもののためのものです(VMにはピーナッツアレルギーがあり、誰かがピーナッツの瓶を落としました)
  • RuntimeException プログラマーが間違ったことに使用されます(プログラマーが配列の最後を離れてウォークしました)
  • Exception(を除くRuntimeException)は、プログラマーの制御が及ばないもののためのものです(ファイルシステムへの書き込み中にディスクがいっぱいになり、プロセスのファイルハンドルの制限に達したため、これ以上ファイルを開くことができません)
  • Throwable 単にすべての例外タイプの親です。

私がよく耳にする議論は、例外が発生した場合、開発者はすべてプログラムを終了することです。

私がよく耳にするもう1つの主張は、チェックされた例外はコードのリファクタリングを難しくするというものです。

「私がやろうとしていることはすべて終了する」という議論の場合、終了する場合でも、適切なエラーメッセージを表示する必要があるということです。エラーの処理に注意を払っているだけの場合、プログラムが終了したときにユーザーは、理由を明確に示さずに過度に満足することはありません。

「リファクタリングが難しくなる」群衆にとって、それは適切な抽象化レベルが選択されなかったことを示しています。宣言はメソッドが投げるのではなくIOExceptionIOExceptionより多くの何が起こっているに適している例外に変換する必要があります。

Mainのラップに問題はありませんcatch(Exception)(または、場合によってcatch(Throwable)は、プログラムが正常に終了できるようにするために-しかし、常に必要な特定の例外をキャッチします。これにより、少なくとも適切な表示を行うことができますエラーメッセージ。

人々が決して答えない質問はこれです:

RuntimeException サブクラスではなくサブクラスをスローする場合、Exception 何をキャッチすることになっているのかをどのようにして知るのですか?

答えがcatchの場合は、Exceptionシステム例外と同じ方法でプログラマエラーも処理しています。それは私には間違っているようです。

キャッチThrowableすると、システム例外とVMエラー(など)を同じように処理していることになります。それは私には間違っているようです。

答えが、スローされていることがわかっている例外だけをキャッチすることである場合、どのような例外がスローされているかをどのようにして知るのですか?プログラマXが新しい例外をスローし、それをキャッチするのを忘れた場合はどうなりますか?それは私にとって非常に危険なようです。

スタックトレースを表示するプログラムは間違っていると思います。チェックされた例外を好まない人はそのように感じませんか?

それで、チェックされた例外が気に入らない場合は、なぜそうしないのかを説明し、答えられない質問に答えてください。

編集:私はどちらのモデルをいつ使用するかについてのアドバイスを探していません、私が探しているのは人々がそこRuntimeExceptionから拡張するのが好きではないために拡張する理由Exceptionおよび/または例外をキャッチしてからスローをRuntimeException追加するのではなく、再スローする理由です彼らの方法。チェック例外を嫌う動機を理解したい。


46
私はそれが完全に主観的であるとは思いません-それは誰もが自分のために何が何であるかを決めるのではなく、特定の用途を持つように設計された言語機能です。そしてそれは特に議論の余地はありません、それは人々が簡単に思いつくことができたであろう特定の反論を前もって扱います。
Gareth、

6
いい加減にして。このトピックは言語機能と見なされ、客観的な方法でアプローチされてきました。
カートSchelfthout 2009年

6
@cletus「あなた自身の質問に答える」もし答えがあったら、私は質問しなかっただろう!
TofuBeer 2009年

8
すばらしい質問です。C ++ではチェック例外はまったくありません。私の意見では、例外機能は使用できなくなります。何かがスローされるかどうかわからないため、実行するすべての関数呼び出しをキャッチする必要があります。
Dimitri C.

4
チェック例外について私が知っている最も強力な議論は、それらが元々Javaにあったのではなく、それらが導入されたときにJDKの何百ものバグを発見したということです。これはJava 1.0より少し前のことです。私は個人的に彼らなしではいられないでしょう、そしてこれについてブルース・エッケルと他の人々と激しく反対します。
ローンの侯爵

回答:


277

私はあなたがしたのと同じブルース・エッケルのインタビューを読んだと思います-それはいつも私を悩ませています。実際、この議論はインタビュー対象者によって行われました(これが本当にあなたが話している投稿である場合)。.NETとC#の背後にあるMSの天才であるAnders Hejlsbergです。

http://www.artima.com/intv/handcuffs.html

私はHejlsbergと彼の作品のファンですが、この議論は常に私を偽物として感じました。基本的には次のようになります。

「チェックされた例外は悪いです。なぜなら、プログラマーは常にそれらをキャッチして却下することによって悪用するだけであり、そうでなければユーザーに提示されるはずの問題が隠されて無視されることにつながるからです。」

「それ以外の場合は、ユーザに提示」あなたは実行時例外を使用する場合、私は意味怠惰なプログラマはそれ(空のcatchブロックでそれをキャッチ対)は無視されますと、ユーザはそれを見ることができます。

議論の要約の要約は、「プログラマーはそれらを適切に使用せず、適切に使用しないことはそれらを持たないことよりも悪い」ということです。

この議論にはいくつかの真実があり、実際、GoslingsのJavaでオペレーターによるオーバーライドを行わない動機は、同様の議論にあると考えています。

しかし、結局のところ、私はそれがHejlsbergの偽の議論であり、おそらく考え抜かれた決定ではなく、欠如を説明するために作成された事後的なものであることに気づきます。

チェックされた例外の過剰使用は悪いことであり、ユーザーによるずさんな処理につながる傾向がありますが、それらを適切に使用することで、APIプログラマーはAPIクライアントプログラマーに大きな利益をもたらすことができると私は主張します。

APIプログラマーは、チェックされた例外をあちこちに投げないように注意する必要があります。そうしないと、クライアントプログラマーを困らせるだけです。非常に怠惰なクライアントプログラマは(Exception) {}、Hejlsbergが警告するようにキャッチすることに頼り、すべての利益が失われ、地獄が続くでしょう。ただし、状況によっては、適切なチェック例外の代わりになるものはありません。

私にとって、古典的な例はファイルを開くAPIです。言語の歴史にあるすべてのプログラミング言語(少なくともファイルシステム上)には、ファイルを開くためのAPIがどこかにあります。そして、このAPIを使用するすべてのクライアントプログラマーは、開こうとしているファイルが存在しない場合に対処する必要があることを知っています。つまり、このAPIを使用するすべてのクライアントプログラマは、このケースに対処する必要があることを知っている必要があります。そして、問題があります。APIプログラマーは、コメントだけで対処する必要があることをAPIプログラマーが支援できるか、またはクライアントが実際に対処するように主張できるかです。

Cでは、イディオムは次のようになります。

  if (f = fopen("goodluckfindingthisfile")) { ... } 
  else { // file not found ...

where fopenは0を返すことで失敗を示し、Cは(愚かに)0をブール値として扱うことができます。基本的に、このイディオムを学習すれば大丈夫です。しかし、あなたが初心者でイディオムを学んでいない場合はどうでしょう。そして、もちろん、あなたは

   f = fopen("goodluckfindingthisfile");
   f.read(); // BANG! 

そして、難しい方法を学びます。

ここでは、厳密に型指定された言語についてのみ説明していることに注意してください。APIが厳密に型指定された言語であるという明確なアイデアがあります。それは、明確に定義されたプロトコルごとに使用する機能(メソッド)のほんの一部です。

その明確に定義されたプロトコルは、通常、メソッドシグネチャによって定義されます。ここで、fopenには文字列(またはCの場合はchar *)を渡す必要があります。それ以外のものを指定すると、コンパイル時エラーが発生します。プロトコルに従っていない-APIを適切に使用していない。

一部の(あいまいな)言語では、戻り値の型もプロトコルの一部です。fopen()一部の言語で同等のものを変数に割り当てずに呼び出そうとすると、コンパイル時エラーも発生します(これはvoid関数でのみ実行できます)。

私が言おうとしている点は次のとおりです。静的に型付けされた言語では、APIプログラマーは、明らかな間違いを犯した場合にクライアントコードがコンパイルされないようにすることで、クライアントがAPIを適切に使用するように促します。

(Rubyのような動的に型付けされた言語では、フロートなど、何でもファイル名として渡すことができます-そしてコンパイルされます。メソッドの引数を制御するつもりがないのに、チェックされた例外でユーザーを煩わせるのはなぜですか。ここで行われる引数は、静的型付け言語にのみ適用されます。)

では、チェックされた例外はどうですか?

これが、ファイルを開くために使用できるJava APIの1つです。

try {
  f = new FileInputStream("goodluckfindingthisfile");
}
catch (FileNotFoundException e) {
  // deal with it. No really, deal with it!
  ... // this is me dealing with it
}

そのキャッチを参照してください?そのAPIメソッドのシグネチャは次のとおりです。

public FileInputStream(String name)
                throws FileNotFoundException

これFileNotFoundException確認済みの例外です。

APIプログラマはこれを次のように言っています。「このコンストラクタを使用して新しいFileInputStreamを作成できますが、

a)ファイル名を文字列として渡す必要がある
b)実行時にファイルが見つからない可能性を受け入れる必要がある

そして、私にとっては、それが重要なポイントです。

鍵となるのは、基本的に、質問が「プログラマーの制御の及ばないもの」と述べていることです。私が最初に思ったのは、彼/彼女はAPIプログラマーが制御できないことを意味するということでした。しかし実際には、適切に使用された場合のチェック例外は、実際にはクライアントプログラマーとAPIプログラマーの両方の制御の及ばないもののためのものであるべきです。これが、チェックされた例外を悪用しないための鍵だと思います。

ファイルを開くことは、ポイントをうまく説明していると思います。APIプログラマーは、APIが呼び出されたときに存在しないことが判明したファイル名をユーザーに与える可能性があること、およびユーザーが必要なものを返すことはできないが、例外をスローする必要があることを知っています。また、これはかなり定期的に発生し、クライアントプログラマーは呼び出しを書き込んだ時点でファイル名が正しいことを期待しているかもしれませんが、実行時の制御が及ばない理由で間違っている可能性があることも知っています。

したがって、APIはそれを明示的にします。私に電話をかけたときにこのファイルが存在せず、うまく処理できた場合があります。

これは、逆のケースでより明確になります。テーブルAPIを書いているところを想像してみてください。このメソッドを含むAPIのどこかにテーブルモデルがあります。

public RowData getRowData(int row) 

APIプログラマーとして、一部のクライアントが行の負の値またはテーブルの外の行の値を渡す場合があることを知っています。そのため、チェックされた例外をスローしてクライアントに強制的に処理させたいと思うかもしれません。

public RowData getRowData(int row) throws CheckedInvalidRowNumberException

(もちろん、「チェック済み」とは呼びません。)

これは、チェック済み例外の悪用です。クライアントコードは、行データをフェッチするための呼び出しでいっぱいになります。そのすべてが、try / catchを使用する必要があります。間違った行が検索されたことをユーザーに報告しますか?おそらくそうではありません。テーブルビューを囲むUIが何であれ、ユーザーが不正な行が要求されている状態になることはないはずです。つまり、クライアントプログラマー側のバグです。

APIプログラマーは、クライアントがそのようなバグをコーディングし、のようなランタイム例外で処理する必要があることを予測できますIllegalArgumentException

でのチェック済み例外を除いてgetRowData、これは明らかに、Hejlsbergの怠惰なプログラマーが単に空のキャッチを追加することにつながるケースです。これが発生すると、テスターやクライアントの開発者がデバッグする場合でも、不正な行の値が明確にならず、ソースを特定するのが難しいノックオンエラーが発生します。アリアンヌロケットは打ち上げ後に爆破します。

さて、ここに問題があります:チェックされた例外FileNotFoundExceptionは良いことだけではなく、クライアントプログラマにとって最も有用な方法でAPIを定義するためのAPIプログラマツールボックスの不可欠なツールだと言っています。しかし、これCheckedInvalidRowNumberExceptionは大きな不便であり、プログラミングの不良につながり、回避する必要があります。しかし、違いを見分ける方法。

私はそれが正確な科学ではないと思います、そして私はおそらく、ヘイルスバーグの議論の根底にあり、おそらくある程度正当化するでしょう。しかし、私はここでお風呂で赤ちゃんを捨てるのは幸せではないので、ここでいくつかのルールを抽出して、良好なチェック例外と不良を区別します。

  1. クライアントの制御不能またはクローズとオープンの違い:

    チェックされた例外は、エラーがAPI クライアントプログラマの両方から制御できない場合にのみ使用してください。これは、システムがどの程度開いているか、または閉じているかに関係しています。クライアントプログラマーが、たとえばテーブルビュー(クローズドシステム)から行を追加および削除するすべてのボタン、キーボードコマンドなどを制御できる制約付き UI では、データをフェッチしようとすると、クライアントプログラミングのバグになります。存在しない行。任意の数のユーザー/アプリケーションがファイルを追加および削除できるファイルベースのオペレーティングシステム(オープンシステム)では、クライアントが要求しているファイルがユーザーの知らないうちに削除された可能性があるため、対処することが期待されます。 。

  2. ユビキタス:

    チェック例外は、クライアントが頻繁に行うAPI呼び出しでは使用しないでください。頻繁にということは、クライアントコードの多くの場所からということです。そのため、クライアントコードは同じファイルを何度も開こうとはしませんが、テーブルビューはRowDataさまざまな方法でさまざまな場所に配置されます。特に、私は多くのコードを書くつもりです

    if (model.getRowData().getCell(0).isEmpty())

    そして毎回try / catchでラップしなければならないのは面倒です。

  3. ユーザーへの通知:

    チェックされた例外は、エンドユーザーに表示される有用なエラーメッセージを想像できる場合に使用してください。これが「それが起こったらどうしますか?」私が上で提起した質問。これは項目1にも関連しています。クライアントAPIシステムの外部の何かが原因でファイルが存在しない可能性があることを予測できるため、ユーザーにそれについて合理的に伝えることができます。

    "Error: could not find the file 'goodluckfindingthisfile'"

    不正な行番号は内部のバグが原因であり、ユーザーの過失がないため、実際に提供できる有用な情報はありません。アプリがランタイム例外をコンソールに渡さない場合、おそらく次のような醜いメッセージが表示されます。

    "Internal error occured: IllegalArgumentException in ...."

    つまり、クライアントプログラマーがユーザーに役立つ方法で例外を説明できないと思われる場合は、おそらくチェック例外を使用しないでください。

だから、それらは私のルールです。やや工夫されており、間違いなく例外があります(可能であれば、私がそれらを調整するのを手伝ってください)。しかし、私の主な議論はFileNotFoundException、checked例外がパラメーター型と同じくらい重要でAPIコントラクトの一部として役立つような場合があるということです。それで、それが誤用されているからといってそれを省略すべきではありません。

申し訳ありませんが、これをそれほど長くて気まぐれにするという意味ではありませんでした。2つの提案で終わります。

A:APIプログラマー:チェックされた例外は、有用性を維持するために控えめに使用してください。疑わしい場合は、チェックされていない例外を使用してください。

B:クライアントプログラマー:開発の早い段階で、ラップされた例外(google it)を作成する習慣をつけます。JDK 1.4以降では、RuntimeExceptionこのためのコンストラクタが提供されていますが、独自のコンストラクタも簡単に作成できます。これがコンストラクタです:

public RuntimeException(Throwable cause)

次に、チェックされた例外を処理する必要があり、怠惰に感じている(または、APIプログラマーが最初にチェックされた例外の使用に熱心であると思っている)場合は、常に習慣を身に付け、例外を飲み込むだけではなく、ラップしますそしてそれを投げ直します。

try {
  overzealousAPI(thisArgumentWontWork);
}
catch (OverzealousCheckedException exception) {
  throw new RuntimeException(exception);  
}

これをIDEの小さなコードテンプレートの1つに入れて、怠惰に感じているときに使用します。このようにして、本当にチェックされた例外を処理する必要がある場合、実行時に問題が発生した後に戻って処理する必要があります。なぜなら、私(およびAnders Hejlsberg)を信じて、あなたがそのTODOに戻ることは決してないからです。

catch (Exception e) { /* TODO deal with this at some point (yeah right) */}

110
実際にファイルを開くことは、完全に逆効果のチェック例外の良い例です。ほとんどの場合、ファイルを開くコードは、例外に関して何の役にも立たないので、コールスタックの数層上で行う方が適切です。チェックされた例外は、メソッドシグネチャを乱雑にすることを強制するので、とにかくやったことを行うことができます。そうすることが最も理にかなった場所で例外を処理してください。
Michael Borgwardt、

116
@Michael:一般に、これらのIO例外を数レベル高いレベルで処理する可能性があることに同意します。しかし、APIクライアントとしてこれらを予期する必要あることを否定することはないようです。このため、チェック例外は適切だと思います。ええ、コールスタックの各メソッドで「スロー」を宣言する必要がありますが、これが煩雑であることに同意しません。これは、メソッドシグネチャの有用な宣言情報です。あなたが言っていること:私の低レベルのメソッドは欠落しているファイルに遭遇するかもしれませんが、それらの処理はあなたに任せます。私は失うものは何もなく、得るべき良い、きれいなコードだけを見ています
ルバーブ

23
@Rhubarb:+1、非常に興味深い答えは、双方の議論を示しています。すごい。最後のコメントについて:コールスタックの各メソッドでスローを宣言することは、特にインターフェイスを途中で実装している場合は、常に可能であるとは限らないことに注意してください。
R.マルティーニョフェルナンデス

8
これは非常に強力な議論です(そして私は一般的に同意します)が、機能しない場合があります:一般的なコールバックです。ユーザー定義コードを呼び出すライブラリーがある場合、そのライブラリーが、それが呼び出すユーザーコードから、それを呼び出すユーザーコードにチェック済み例外を伝播する方法はありません(私が知っているように、Javaでは)。この問題は、適切なジェネリック型をサポートしていない多くの静的型付け言語の型システムの他の部分にも及びます。この回答は、これについて言及することで改善されるでしょう(おそらく回答も)。
マンカルス2011

7
@Rhubarbが間違っています。チェックされた例外は、処理できる&すべきである「不測の事態」を意図したものでしたが、「早期にスローし、遅くキャッチする」ベストプラクティスと常に互換性がありませんでした。ファイルを開くことは、適切に処理された例を示しています。ほとんどのIOException(バイトの書き込みなど)、SQLException、RemoteExceptionは、意味のある方法で処理することは不可能です。失敗または再試行は「ビジネス」または「リクエスト」レベルで行う必要があり、Javaライブラリで使用されているように、チェックされた例外は間違いであり、これを困難にします。literatejava.com/exceptions/...
トーマス・W

184

チェック例外については、通常の概念の理解では実際には例外ではないということです。代わりに、APIの代替戻り値です。

例外の全体的な考え方は、コールチェーンのどこかでスローされたエラーは、途中のコードが気にすることなく、バブルアップして、どこか別の場所のコードで処理できるということです。一方、チェック済み例外では、スローアーとキャッチャーの間のすべてのレベルのコードが、例外を通過できるすべての形式の例外について知っていることを宣言する必要があります。これは実際には、チェックされた例外が呼び出し側がチェックしなければならない特別な戻り値である場合とほとんど変わりません。例:[疑似コード]:

public [int or IOException] writeToStream(OutputStream stream) {
    [void or IOException] a= stream.write(mybytes);
    if (a instanceof IOException)
        return a;
    return mybytes.length;
}

Javaは代替の戻り値、または単純なインラインタプルを戻り値として実行できないため、チェックされた例外は妥当な応答です。

問題は、標準ライブラリの大規模なスワスを含む多くのコードが、チェックされた例外を実際の例外的な条件に誤用し、いくつかのレベルをキャッチしたいということです。IOExceptionがRuntimeExceptionではないのはなぜですか?他のすべての言語では、IO例外を発生させることができ、それを処理するために何もしないと、アプリケーションが停止し、便利なスタックトレースが表示されます。これは起こり得る最高のことです。

例として、ストリームへの書き込みプロセス全体からすべてのIOExceptionをキャッチし、プロセスを中止してエラー報告コードにジャンプする2つの方法が考えられます。Javaでは、すべての呼び出しレベルでIOExceptionをスローせずにそれを行うことはできません。そのようなメソッドは、例外処理について知っている必要はありません。署名に例外を追加する必要があります:

  1. カップリングを不必要に増やします。
  2. インターフェイスの署名を変更するのが非常に脆弱になります。
  3. コードを読みにくくします。
  4. プログラマーの一般的な反応は、「例外のスロー」、「キャッチ(例外e){}」などの恐ろしいことを実行したり、すべてをRuntimeExceptionでラップしたり(デバッグを困難にする)することです。

そして、次のようなとんでもないライブラリ例外がたくさんあります:

try {
    httpconn.setRequestMethod("POST");
} catch (ProtocolException e) {
    throw new CanNeverHappenException("oh dear!");
}

このように馬鹿げたコードでコードを乱雑にする必要がある場合、実際にはこれは単純な貧弱なAPI設計ですが、チェックされた例外が大量のヘイトを受け取るのも不思議ではありません。

もう1つの特定の悪影響は、コンポーネントAがジェネリックコンポーネントBにコールバックを提供する制御の反転です。コンポーネントAは、コールバックからコンポーネントBを呼び出した場所に例外をスローできるようにしたいが、できません。これは、Bによって修正されるコールバックインターフェイスを変更するためです。Aは、実際の例外をRuntimeExceptionでラップすることによってのみ実行できます。

Javaとその標準ライブラリに実装されているチェック済み例外とは、ボイラープレート、ボイラープレート、ボイラープレートを意味します。すでに冗長な言語では、これは勝利ではありません。


14
コードの例では、ログを読み取るときに元の原因を見つけることができるように、例外をチェーンするのが最適です。throw CanNeverHappenException(e);
Esko Luontola 2009年

5
同意しません。チェックされているかどうかにかかわらず、例外は例外的な条件です。例:HTTP経由でオブジェクトを取得するメソッド。戻り値はそれ以外の場合はオブジェクトか何もありません。失敗する可能性のあるものはすべて例外的です。Cで行われているようにそれらを戻り値として扱うことは、混乱と不十分な設計につながるだけです。
ミスタースミス

15
@ミスター:私が言っているのは、Javaに実装されたチェック例外は、実際には、C ++や他のJava以前の言語で認識される従来の「例外」よりも、Cのような戻り値のように動作するということです。そして、そのIMOは確かに混乱と貧弱な設計につながります。
ボビンス、2011

9
標準ライブラリがチェック済み例外を誤用することで、間違いなく混乱と不正な捕捉動作が追加されることに同意します。そして、多くの場合、それは不十分なドキュメントによるものです。たとえば、「他のI / Oエラーが発生した」ときにIOExceptionをスローするdisconnect()などの破棄メソッドです。さて、私は切断していた!ハンドルやその他のリソースをリークしていますか?再試行する必要がありますか?なぜ起こったのわからないので、どうしたらいいのわからないので、飲み込むのか、再試行するのか、保釈するのかを推測する必要があります。
charstar

14
「API代替戻り値」の+1。チェックされた例外を見る興味深い方法。
ZsoltTörök2011

75

チェックされた例外に対するすべての(多くの)理由を再ハッシュするのではなく、1つだけ選びます。このコードブロックを書いた回数のカウントを失いました。

try {
  // do stuff
} catch (AnnoyingcheckedException e) {
  throw new RuntimeException(e);
}

99%何もできません。最後に、ブロックは必要なクリーンアップを実行します(または少なくとも実行する必要があります)。

私はこれを見た回数のカウントも失いました:

try {
  // do stuff
} catch (AnnoyingCheckedException e) {
  // do nothing
}

どうして?誰かがそれに対処しなければならず、怠惰だったからです。それは間違っていましたか?承知しました。それは起こりますか?もちろんです。代わりにこれが未チェックの例外であった場合はどうなりますか?アプリが死んだだけです(例外を飲み込むよりも望ましい)。

そして、java.text.Formatのように、フロー制御の形式として例外を使用する腹立たしいコードがあります。Bzzzt。違う。フォームの数値フィールドに「abc」を入力するユーザーも例外ではありません。

OK、それは3つの理由だったと思います。


4
ただし、例外が適切にキャッチされた場合は、ユーザーに通知し、他のタスク(ログ?)を実行して、制御された方法でアプリケーションを終了できます。一部のAPIパーツはよりよく設計されている可能性があることに同意します。そして、怠惰なプログラマーの理由から、まあ、私はプログラマーとしてあなたのコードに対して100%責任があると思います。
ミスタースミス

3
try-catch-rethrowを使用すると、メッセージを指定できることに注意してください。通常、これを使用して、状態変数の内容に関する情報を追加します。よくある例は、IOExceptionが問題のファイルのabsolutePathName()を追加する場合です。
するThorbjörnRavnアンデルセン

15
EclipseなどのIDEは、空のcatchブロックを確認した回数に多くの責任があると思います。実際には、デフォルトで再スローする必要があります。
artbristol 2013年

12
「99%何もできません」-間違っています。アプリをクラッシュさせるだけでなく、「サーバーに接続できませんでした」または「IOデバイスに障害が発生しました」というメッセージをユーザーに表示できます少しのネットワーク障害のため。あなたの例は両方とも、悪いプログラマーによる芸術です。あなたは悪いプログラマを攻撃するべきであり、例外自体をチェックするべきではありません。インスリンドレッシングとして使用するときに、糖尿病の助けにならないためにインスリンを攻撃するようなものです。
AxiomaticNexus 14年

2
@YasmaniLlanesあなたはいつもこれらのことをすることはできません。時々、あなたが従うべきインターフェースを持っています。そして、これは、保守性の高いAPIを設計する場合に特に当てはまります。なぜなら、いたるところに副作用を投げ始めることができないからです。それと、それがもたらす複雑さの両方が、大規模にあなたをひどく噛み締めます。ですから、はい、99%の確率で、何もする必要はありません。
MasterMastic 2015

50

私はこれが古い質問であることを知っていますが、チェックされた例外と格闘するのにしばらく費やしており、追加するものがあります。その間はご容赦ください!

チェックされた例外を持つ私の主な牛肉は、それらが多形性を台無しにすることです。ポリモーフィックインターフェイスでうまく機能させることは不可能です。

古き良きJava Listインターフェースを取る。ArrayListおよびのような一般的なインメモリ実装がありLinkedListます。また、AbstractList新しいタイプのリストを簡単に設計できるスケルトンクラスもあります。読み取り専用リストの場合、2つのメソッドのみを実装する必要があります:size()get(int index)

この例のWidgetListクラスWidgetは、ファイルからいくつかの固定サイズのオブジェクト(図にはありません)を読み取ります。

class WidgetList extends AbstractList<Widget> {
    private static final int SIZE_OF_WIDGET = 100;
    private final RandomAccessFile file;

    public WidgetList(RandomAccessFile file) {
        this.file = file;
    }

    @Override
    public int size() {
        return (int)(file.length() / SIZE_OF_WIDGET);
    }

    @Override
    public Widget get(int index) {
        file.seek((long)index * SIZE_OF_WIDGET);
        byte[] data = new byte[SIZE_OF_WIDGET];
        file.read(data);
        return new Widget(data);
    }
}

使い慣れたListインターフェースを使用してウィジェットを公開することで、自分自身を知らなくてもアイテムを取得(list.get(123))またはリストを反復()できます。このリストを、ジェネリックリストを使用する標準メソッドに渡すか、でラップすることができます。それを使用するコードは、「ウィジェット」がその場で構成されているのか、配列から来ているのか、ファイルまたはデータベースから読み取られているのか、ネットワーク全体から、または将来のサブスペースリレーから読み取られているのかを知る必要もありません。インターフェースが正しく実装されているため、正しく機能します。for (Widget w : list) ...WidgetListCollections.synchronizedListList

そうではありませんが。上記のクラスはコンパイルできません。ファイルアクセスメソッドIOExceptionが、「キャッチまたは指定」する必要があるチェック済み例外であるをスローする可能性があるためです。スローされたものとして指定することはできません - Listインターフェースの規約に違反するため、コンパイラーは許可しません。そして、WidgetListそれ自体が例外を処理できる便利な方法はありません(後で詳しく説明します)。

どうやら唯一のことは、チェックされた例外をいくつかのチェックされていない例外としてキャッチして再スローすることです:

@Override
public int size() {
    try {
        return (int)(file.length() / SIZE_OF_WIDGET);
    } catch (IOException e) {
        throw new WidgetListException(e);
    }
}

public static class WidgetListException extends RuntimeException {
    public WidgetListException(Throwable cause) {
        super(cause);
    }
}

((編集:Java 8はUncheckedIOException、まさにこのケースのためのクラスを追加しました:IOExceptionポリモーフィックメソッドの境界を越えてsをキャッチして再スローするため。一種の私のポイントを証明します!))

したがって、チェックされた例外、このような場合には機能ません。それらを投げることはできません。Mapデータベースに裏打ちされた賢い、またはjava.util.RandomCOMポートを介して量子エントロピーソースに接続された実装の同上。ポリモーフィックインターフェイスの実装で何か新しいことを行おうとすると、チェックされた例外の概念は失敗します。しかし、チェックされた例外は非常に狡猾であり、コードを乱雑にしてスタックトレースを乱雑にして、低レベルのメソッドから何かをキャッチして再スローする必要があるので、それでも安心できません。

Runnableチェックされた例外をスローするものを呼び出すと、ユビキタスインターフェースがこのコーナーに戻ることがよくあります。例外をそのままスローすることはできないため、としてキャッチして再スローすることにより、コードを乱雑にすることができますRuntimeException

実際には、ハックを使用すると、宣言されていないチェック例外スローできます。実行時、JVMはチェックされた例外ルールを気にしないので、コンパイラーだけをだます必要があります。これを行う最も簡単な方法は、ジェネリックを乱用することです。これは私のメソッドです(クラス名は(Java 8より前のバージョンでは)ジェネリックメソッドの呼び出し構文で必要なためです)。

class Util {
    /**
     * Throws any {@link Throwable} without needing to declare it in the
     * method's {@code throws} clause.
     * 
     * <p>When calling, it is suggested to prepend this method by the
     * {@code throw} keyword. This tells the compiler about the control flow,
     * about reachable and unreachable code. (For example, you don't need to
     * specify a method return value when throwing an exception.) To support
     * this, this method has a return type of {@link RuntimeException},
     * although it never returns anything.
     * 
     * @param t the {@code Throwable} to throw
     * @return nothing; this method never returns normally
     * @throws Throwable that was provided to the method
     * @throws NullPointerException if {@code t} is {@code null}
     */
    public static RuntimeException sneakyThrow(Throwable t) {
        return Util.<RuntimeException>sneakyThrow1(t);
    }

    @SuppressWarnings("unchecked")
    private static <T extends Throwable> RuntimeException sneakyThrow1(
            Throwable t) throws T {
        throw (T)t;
    }
}

ばんざーい!これを使用すると、宣言したり、ラップしRuntimeExceptionたりせずに、スタックトレースを乱雑にすることなく、チェックされた例外をスタックの深さまで投げることができます。「WidgetList」の例をもう一度使用します。

@Override
public int size() {
    try {
        return (int)(file.length() / SIZE_OF_WIDGET);
    } catch (IOException e) {
        throw sneakyThrow(e);
    }
}

残念ながら、チェック例外の最後の侮辱は、コンパイラーがチェック例外をスローすることができなかった場合に、その例外をキャッチすることを拒否することです。(チェックされていない例外にはこのルールはありません。)こっそりとスローされた例外をキャッチするには、次のようにする必要があります。

try {
    ...
} catch (Throwable t) { // catch everything
    if (t instanceof IOException) {
        // handle it
        ...
    } else {
        // didn't want to catch this one; let it go
        throw t;
    }
}

これは少し厄介ですが、プラス面では、にラップされたチェック済み例外を抽出するコードよりも少し単純ですRuntimeException

幸いthrow t;なことに、tJava 7で追加されたキャッチされた例外の再スローに関するルールのおかげで、タイプがチェックされている場合でも、ステートメントはここで合法です。


チェックされた例外がポリモーフィズムを満たす場合、反対のケースも問題になります。メソッドがチェックされた例外をスローする可能性があると指定されているが、オーバーライドされた実装ではスローされない場合です。たとえば、抽象クラスOutputStreamwriteメソッドはすべて指定しますthrows IOExceptionByteArrayOutputStream真のI / Oソースの代わりにメモリ内配列に書き込むサブクラスです。オーバーライドされたwriteメソッドはを引き起こすことができないIOExceptionため、それらにはthrows節がなく、catch-or-specify要件を心配せずにそれらを呼び出すことができます。

いつもとは限りません。Widgetそれをストリームに保存するメソッドがあるとします:

public void writeTo(OutputStream out) throws IOException;

このメソッドをプレーンを受け入れるように宣言することOutputStreamは正しいことです。そのため、ファイル、データベース、ネットワークなど、あらゆる種類の出力で多態的に使用できます。そして、インメモリ配列。ただし、メモリ内配列では、実際には起こり得ない例外を処理するための偽の要件があります。

ByteArrayOutputStream out = new ByteArrayOutputStream();
try {
    someWidget.writeTo(out);
} catch (IOException e) {
    // can't happen (although we shouldn't ignore it if it does)
    throw new RuntimeException(e);
}

いつものように、チェックされた例外は邪魔になります。変数がより制限のない例外要件を持つ基本型として宣言されている場合、アプリケーションで発生しないことがわかっている場合でも、それらの例外のハンドラーを追加する必要があります。

しかし、待ってください、チェックされた例外は実際には非常に煩わしいので、逆のこともできません!現在IOException、のwrite呼び出しによってスローされたものをすべてキャッチしたOutputStreamが、変数の宣言された型をに変更したいとします。ByteArrayOutputStreamコンパイラーは、スローできないというチェックされた例外をキャッチしようとするとあなたを怒らせます。

そのルールはいくつかの不条理な問題を引き起こします。例えば、3つのいずれかwriteの方法はOutputStreamされていないことで上書きByteArrayOutputStream。具体的にwrite(byte[] data)write(byte[] data, int offset, int length)、オフセット0と配列の長さで呼び出して配列全体を書き込む便利なメソッドです。ByteArrayOutputStream3つの引数を持つメソッドをオーバーライドしますが、1つの引数を持つ簡易メソッドをそのまま継承します。継承されたメソッドは正確に動作しますが、不要なthrows句が含まれています。これはおそらくの設計の見落としByteArrayOutputStreamでしたが、例外をキャッチするコードとのソース互換性が失われるため、修正することはできません。

そのルールは、編集やデバッグの際にも迷惑です。たとえば、メソッド呼び出しを一時的にコメントアウトすることがあり、チェックされた例外がスローされた可能性がある場合、コンパイラはローカルtrycatchブロックの存在について文句を言うようになります。したがって、これらもコメントアウトする必要があります。また、コードを編集する{}、コメントアウトされているため、IDEは間違ったレベルにインデントします。ガ!これは小さな不満ですが、チェックされた例外がこれまでに行った唯一のことは問題を引き起こすようです。


あと少しで完了です。チェックされた例外に対する私の最後のフラストレーションは、ほとんどの通話サイトで、それらを使用して実行できる便利なものがないということです。理想的には、問題が発生したときに、ユーザーに問題を通知したり、必要に応じて操作を終了または再試行したりできる、アプリケーション固有の適切なハンドラーを用意します。スタックの上位にあるハンドラーだけがこれを行うことができます。それは、全体的な目標を知っているのはそれだけだからです。

代わりに、次のイディオムを取得します。これは、コンパイラをシャットダウンする方法としてはびこっています。

try {
    ...
} catch (SomeStupidExceptionOmgWhoCares e) {
    e.printStackTrace();
}

GUIまたは自動化プログラムでは、印刷されたメッセージは表示されません。さらに悪いことに、例外の後でコードの残りの部分を実行します。例外は実際にはエラーではありませんか?その後、印刷しないでください。そうしないと、他の何かが一瞬で爆発し、その時までに元の例外オブジェクトは消えてしまいます。このイディオムはBASIC On Error Resume NextやPHPに勝るものはありませんerror_reporting(0);

ある種のロガークラスを呼び出すことはあまり良くありません:

try {
    ...
} catch (SomethingWeird e) {
    logger.log(e);
}

これはe.printStackTrace();、コードが不確定状態であるのと同じくらい怠惰で、耕作を続けています。さらに、特定のロギングシステムやその他のハンドラーの選択はアプリケーション固有であるため、コードの再利用が困難になります。

ちょっと待って!アプリケーション固有のハンドラーを見つける簡単で普遍的な方法があります。呼び出しスタックの上位にあります(または、スレッドのキャッチされない例外ハンドラーとして設定されています)。したがって、ほとんどの場所では、スタックの上位に例外をスローするだけで済みます。例えば、throw e;。チェックされた例外は邪魔になります。

言語が設計されたときにチェック例外は良い考えのように聞こえたのは確かですが、実際には、それらはすべて面倒であり、利益がないことがわかりました。


WidgetListを使用したサイズメソッドの場合、サイズを変数にキャッシュし、コンストラクターで設定します。コンストラクタは自由に例外をスローできます。ただし、WidgetListを使用しているときにファイルが変更された場合、これは機能しません。
TofuBeer 2013年

2
SomeStupidExceptionOmgWhoCares誰かがそれを投げるのに十分な世話をした。したがって、それがスローされるべきではなかったか(悪い設計)、または実際に処理する必要があります。残念ながら設計が悪かった1.0より前のクラス(バイト配列出力ストリーム)の実装が悪い場合も同様です。
TofuBeer 2013年

2
適切なイディオムは、ネストされたサブルーチン呼び出しによってスローされた指定の例外をキャッチし、それらをにラップして再スローするディレクティブRuntimeExceptionでした。ルーチンは同時にとして宣言throws IOExceptionすることもできますがIOException、ネストされた呼び出しからスローされたものはすべて予期しないものと見なしてラップする必要があることも指定できます。
スーパーキャット2013

15
私はこの投稿に出くわしたJavaの経験を持つプロのC#開発者です。なぜ誰もがこの奇妙な行動を支持するのかについて、私は困惑しています。.NETで特定のタイプの例外をキャッチしたい場合は、それをキャッチできます。スタックに投げ込ませたいだけなら、何もする必要はありません。私はJavaがそれほど風変わりでなかったことを望みます。:)
aikeru

「メソッドの呼び出しを一時的にコメントアウトすることがある」について- if (false)これを使用する方法を学びました。これはスロー句の問題を回避し、警告により迅速に戻ることができます。+++とはいえ、私はあなたが書いたすべてに同意します。チェック例外にはいくつかの値がありますが、この値はそれらのコストと比較すると無視できます。ほとんどの場合、彼らは邪魔をします。
maaartinus

45

まあ、それはスタックトレースを表示したり、静かにクラッシュすることではありません。それは、レイヤー間でエラーを通信できるようにすることです。

チェック例外の問題は、重要な詳細(つまり、例外クラス)を飲み込むように人々を促すことです。その詳細を飲み込まないことを選択した場合は、アプリ全体にスロー宣言を追加し続ける必要があります。これは、1)新しい例外タイプが多くの関数シグネチャに影響を与えること、および2)実際にキャッチしたい例外の特定のインスタンスを見逃す可能性があることを意味します(たとえば、データをファイル。2次ファイルはオプションであるため、そのエラーは無視できますが、署名があるため、throws IOExceptionこれを見落としがちです)。

私は実際にアプリケーションでこの状況に対処しています。ほとんどの例外をAppSpecificExceptionとして再パッケージ化しました。これにより、署名は本当にクリーンになりthrows、署名の爆発を心配する必要がなくなりました。

もちろん、今度は、より高いレベルでのエラー処理を特化し、再試行ロジックなどを実装する必要があります。ただし、すべてがAppSpecificExceptionであるため、「IOExceptionがスローされた場合は再試行する」または「ClassNotFoundがスローされた場合は完全に中止する」とは言えません。実際の例外に到達するための信頼できる方法はありません。なぜなら、コードとサードパーティのコードとの間でやり取りされるときに、物事が何度もリパッケージされるからです。

これが、Pythonでの例外処理の大ファンである理由です。あなたはあなたが欲しいものだけを捕まえることができ、そして/または処理することができます。あなたがそれを自分で再投げかけたかのように、他のすべてのものは泡立ちます(とにかくそれをしました)。

私は何度も何度も、そして私が言及したプロジェクト全体を通して、例外処理が3つのカテゴリに分類されることを発見しました。

  1. 特定の例外をキャッチして処理します。これは、たとえば再試行ロジックを実装するためです。
  2. 他の例外をキャッチして再スローします。ここで発生するのは、通常はロギングだけであり、通常は「$ filenameを開くことができません」などのささいなメッセージです。これらは、何も実行できないエラーです。より高いレベルだけがそれを処理するのに十分知っています。
  3. すべてをキャッチし、エラーメッセージを表示します。これは通常、ディスパッチャーのルートにあり、例外ではないメカニズム(ポップアップダイアログ、RPCエラーオブジェクトのマーシャリングなど)を介して呼び出し元にエラーを確実に伝えることができます。

5
AppSpecificExceptionの特定のサブクラスを作成して、プレーンなメソッドシグネチャを維持しながら分離できるようにすることもできます。
するThorbjörnRavnアンデルセン

1
また、項目2の非常に重要な追加は、(たとえば、RuntimeExceptionにネストすることによって)キャッチされた例外に情報を追加できることです。スタックトレースでファイルの名前が見つからないほうが、ログファイルの深いところに隠しておくよりはるかに便利です。
するThorbjörnRavnアンデルセン

1
基本的にあなたの議論は「例外の管理は疲れるので、私はそれに対処したくない」です。例外が浮上すると、意味が失われ、コンテキスト作成は実質的に役に立たなくなります。APIの設計者は、問題が発生した場合に何が期待できるかについて契約上明確です。この例外またはその例外が「発生する」と通知されなかったためにプログラムがクラッシュした場合、設計者としてのあなたは失敗し、あなたの失敗の結果、私のシステムは可能な限り安定していません。
ニュートピア、2009年

4
それは私が言っていることではありません。あなたの最後の文は実際に私に同意します。すべてがAppSpecificExceptionでラップされている場合、それはバブルアップしません(そして意味/コンテキストが失われます)。そして、はい、APIクライアントは通知されていません-これは、チェックされた例外で発生するものです(Javaの場合) 、人々は多くのthrows宣言を持つ関数を扱いたくないからです。
Richard Levasseur

6
@Newtopian-例外は主に「ビジネス」または「リクエスト」レベルでのみ処理できます。失敗する可能性があるすべての小さな障害ではなく、大きな粒度で失敗または再試行することは理にかなっています。このため、例外処理のベストプラクティスは、「早期にスローし、遅くキャッチする」と要約されています。チェック例外は、信頼性を正しいレベルで管理することを困難にし、誤ってコーディングされた膨大な数のcatchブロックを奨励します。 literatejava.com/exceptions/...
トーマス・W

24

SNR

まず、チェックされた例外はコードの「信号対雑音比」を減少させます。Anders Hejlsbergは、同様の概念である命令型プログラミングと宣言型プログラミングについても話します。とにかく、次のコードスニペットを検討してください。

Javaの非UIスレッドからUIを更新します。

try {  
    // Run the update code on the Swing thread  
    SwingUtilities.invokeAndWait(() -> {  
        try {
            // Update UI value from the file system data  
            FileUtility f = new FileUtility();  
            uiComponent.setValue(f.readSomething());
        } catch (IOException e) {  
            throw new UncheckedIOException(e);
        }
    });
} catch (InterruptedException ex) {  
    throw new IllegalStateException("Interrupted updating UI", ex);  
} catch (InvocationTargetException ex) {
    throw new IllegalStateException("Invocation target exception updating UI", ex);
}

C#で非UIスレッドからUIを更新します。

private void UpdateValue()  
{  
   // Ensure the update happens on the UI thread  
   if (InvokeRequired)  
   {  
       Invoke(new MethodInvoker(UpdateValue));  
   }  
   else  
   {  
       // Update UI value from the file system data  
       FileUtility f = new FileUtility();  
       uiComponent.Value = f.ReadSomething();  
   }  
}  

それは私にはもっとはっきりしているようです。Swingでより多くのUI作業を開始すると、チェックされた例外は本当に煩わしく、役に立たなくなります。

脱獄

JavaのListインターフェースなどの最も基本的な実装でさえ実装するために、契約による設計のためのツールとして例外を確認しました。データベース、ファイルシステム、またはチェック済み例外をスローするその他の実装によってサポートされているリストについて考えてみます。唯一可能な実装は、チェックされた例外をキャッチして、チェックされていない例外として再スローすることです。

@Override
public void clear()  
{  
   try  
   {  
       backingImplementation.clear();  
   }  
   catch (CheckedBackingImplException ex)  
   {  
       throw new IllegalStateException("Error clearing underlying list.", ex);  
   }  
}  

そして今、あなたはそのすべてのコードのポイントは何ですか?チェックされた例外はノイズを追加するだけであり、例外は捕捉されましたが処理されず、契約による設計(チェックされた例外に関して)は壊れています。

結論

  • 例外のキャッチは、それらの処理とは異なります。
  • チェック例外はコードにノイズを追加します。
  • 例外処理は、C#でも例外処理なしでうまく機能します。

以前これについてブログに書いた。


3
この例では、JavaとC#の両方が、例外を処理せずに例外を伝播させています(IllegalStateExceptionを介したJava)。違いは、FileNotFoundExceptionを処理する必要があるかもしれませんが、InvocationTargetExceptionまたはInterruptedExceptionを処理することはあまりありません。
ルーク・キナン

3
そして、C#の方法で、I / O例外が発生する可能性があることをどのように知ることができますか?また、実行から例外をスローすることはありません...例外処理を乱用することを検討しています。申し訳ありませんが、コードのその部分については、まだあなたの側を見ることができます。
TofuBeer 2009

7
私たちはそこに着いています:-)したがって、APIの新しいリリースごとに、すべての呼び出しをくまなく調べて、発生する可能性のある新しい例外を探す必要がありますか?これは、下位互換性について心配する必要がないため、社内APIの内部で簡単に発生します。
TofuBeer 2009

3
S / N比を下げるということですか?
neo2862 2009年

3
@TofuBeer基になるAPIのインターフェースが変更された後、コードの更新を強制されていませんか?そこにチェックされていない例外しかなかったとしたら、それは知らないうちに壊れた/不完全なプログラムになってしまうでしょう。
フランソワブルジョワ2013

23

アルティマ は、 .NETのアーキテクトの1人であるAnders Hejlsbergへのインタビュー公開しましたは、チェックされた例外に対する議論を鋭くカバーしています。短いテイスター:

throws句は、少なくともJavaでの実装方法では、必ずしも例外を処理する必要はありませんが、処理しない場合は、どの例外が通過するかを正確に確認する必要があります。宣言された例外をキャッチするか、独自のthrows句に入れる必要があります。この要件を回避するために、人々はとんでもないことをします。たとえば、すべてのメソッドを「例外をスロー」で装飾します。それだけで機能が完全に無効になり、プログラマーにもっとむちゃくちゃな書き方をさせただけです。それは誰の助けにもなりません。


2
実際、チーフアーキテクト。
トラップ

18
私はそれを読んだのですが、彼の議論は「世の中には悪いプログラマーがいる」ということになります。
TofuBeer 2009年

6
豆腐ビール、全然。重要なのは、呼び出されたメソッドがスローするExceptionをどう処理すればよいかわからないことが多く、本当に関心のあるケースについては言及されていないことです。ファイルを開くと、たとえばIO例外が発生します...それは私の問題ではないので、それをスローします。ただし、最上位の呼び出しメソッドは処理を停止し、不明な問題があることをユーザーに通知するだけです。チェックされた例外はまったく役に立ちませんでした。それは起こり得る百万の奇妙なことの一つでした。
Dan Rosenstark、2009年

4
@yar、チェックされた例外が気に入らない場合は、「新しいRuntimeException( "Foo.bar()を実行するときにこれを予期していませんでした"、e)をスロー」して、それで完了します。
するThorbjörnRavnアンデルセン

4
@ThorbjørnRavnAndersen:残念ながら.netがコピーしたJavaの根本的な設計上の弱点は、例外のタイプを、それを実行するかどうかを決定するための主要な手段と、一般的なタイプのことを示す主要な手段の両方として使用することです。実際には2つの問題がほぼ直交しているのに、それはうまくいきませんでした。問題なのは問題ではなく、状態オブジェクトの内容です。さらに、.netとJavaはどちらも、デフォルトでは、例外の処理と解決は通常は同じであると想定していますが、実際には異なる場合もあります。
スーパーキャット2012年

20

私は常にチェック済みの例外を支持してきたので、最初はあなたに同意しました。しかし、私はチェックされた例外のように影響しないことに気づきました。

あなたの質問に答えるには、はい、私は自分のプログラムでスタックトレースを表示することを好んでいます。私は、アプリケーションがあなたがこれまでに見たいと思うことができる最も醜いエラーメッセージの恐ろしいヒープに爆発させたいと思っています。

その理由は、それを行う場合は修正する必要があり、すぐに修正する必要があるためです。問題があることをすぐに知りたい。

実際に例外を何回処理しますか?例外のキャッチについて話しているのではなく、例外の処理について話しているのですか?次のように書くのは簡単すぎる。

try {
  thirdPartyMethod();
} catch(TPException e) {
  // this should never happen
}

そして、私はあなたがそれが悪い習慣であり、その「答え」が例外を除いて何かをすることだと言うことができることを知っていると私は知っています(私に推測させてください、それを記録しますか?)が、現実の世界(tm)では、ほとんどのプログラマーはただしませんそれ。

ですから、そうする必要がないのであれば、例外をキャッチしたくありません。また、失敗したときにプログラムを目を見張るように爆破させたいのです。静かに失敗することは、考えられる最悪の結果です。


Javaは、このようなことを行うことをお勧めします。これにより、すべてのメソッドシグネチャにあらゆるタイプの例外を追加する必要がなくなります。
yfeldblum 2009年

17
おかしい..私がチェック例外を正しく受け入れて適切に使用して以来、私のプログラムはあなたの顔の顧客の不満の巨大な湯気の山で爆破するのを止めました。開発中に醜い悪いスタックトレースがある場合、顧客もそれらを取得する必要があります。D'loveは、ボタンXYZのカラー設定を解析できなかったため、ソフトウェアで喜んでハミングする代わりにボタンXYZのカラー設定を解析できなかったという小さなトレイ通知ではなく、クラッシュしたシステムでマイルスタックトレースのArrayIndexOutOfBoundsExceptionが表示されたときに顔を見るのが大好きです。 along
ニュートピア09

1
おそらくJavaに必要なのは、メソッドまたはコードのtry / catchブロックがその中で発生する特定の例外を処理する準備ができていないこと、および明示的以外の方法で発生するそのような例外を指定する「cantHandle」宣言です。 (呼び出されたメソッドではなく)そのメソッド内でのスローは、自動的にラップされ、RuntimeExceptionで再スローされます。私見、チェックされた例外は、ラップされずにコールスタックに伝達されることほとんどありませ
スーパーキャット2013年

3
@Newtopian-私はサーバーと高信頼性ソフトウェアを書き、25年間そうしています。私のプログラムは爆破したことなく、高可用性、再試行と再接続、統合ベースの金融システムと軍事システムを使用しています。ランタイム例外を優先する絶対的な客観的根拠があります。チェックされた例外があると、正しい「早い段階でスローし、遅い段階でキャッチする」ベストプラクティスに従うのが難しくなります。正しい信頼性とエラー処理は、「ビジネス」、「接続」、または「要求」レベルです。(または時々データを解析するとき)。チェックされた例外は、正しい処理を妨げます。
Thomas W

1
ここであなたが話している例外はRuntimeExceptions、実際に捕らえる必要のないものであり、プログラムを爆発させなければならないことに同意します。常にキャッチして処理する必要のある例外は、のようなチェックされた例外ですIOException。を取得した場合IOException、コードで修正するものはありません。ネットワークの問題があったからといって、プログラムが爆破してはなりません。
AxiomaticNexus 14年

20

記事「効果的なJava例外」では、チェックなしの例外を使用する場合とチェック済みの例外を使用する場合について説明しています。主なポイントを強調するためのその記事からの引用は次のとおりです。

不測の事態: メソッドの意図した目的に関して表現できる、メソッドからの代替応答を要求する予期される状態。メソッドの呼び出し元は、これらの種類の条件を想定しており、それらに対処するための戦略を持っています。

フォルト: メソッドの内部実装を参照せずに説明できない意図した目的をメソッドが達成できないようにする予期しない状態。

(SOはテーブルを許可しないため、元のページから以下を読むことをお勧めします ...)

不測の事態

  • と見なされます:デザインの一部
  • 発生が予想されます:定期的ですがまれに
  • 誰が気にするか:メソッドを呼び出す上流のコード
  • 例:代替の戻りモード
  • 最適なマッピング:チェックされた例外

故障

  • と見なされる:厄介な驚き
  • 発生が予想される:なし
  • 誰が気にするか:問題を修正する必要がある人々
  • 例:プログラミングのバグ、ハードウェアの誤動作、構成の誤り、ファイルの欠落、サーバーの利用不可
  • 最適なマッピング:未チェックの例外

私はそれらをいつ使うべきかを知っています、なぜそのアドバイスに従わない人々がそのアドバイスに従わないのか知りたいです:-)
TofuBeer

プログラミングのバグとは何ですか?それらを使用法のバグと区別する方法は?ユーザーがプログラムに間違った引数を渡した場合、それはプログラミングのバグですか?Javaの観点からはプログラミングのバグではないかもしれませんが、シェルスクリプトの観点からはプログラミングのバグです。では、無効な引数はargs[]何ですか?それらは不測の事態ですか、それとも障害ですか?
2013年

3
@TofuBeer-Javaライブラリの設計者は、すべての種類の回復不可能な低レベルのエラーを、チェックされていないはずのはずのチェックされた例外として配置することを選択しました。たとえば、FileNotFoundは、チェックする必要がある唯一のIOExceptionです。JDBCに関しては-データベースへの接続のみであり、不測の事態と考えるのが合理です。他のすべてのSQLExceptionsは失敗し、チェックされていないはずです。エラー処理は、「ビジネス」または「リクエスト」レベルで正しく行う必要があります。「早い段階でスローし、遅い段階でキャッチする」ベストプラクティスを参照してください。チェックされた例外はそれに対する障壁です。
トーマスW

1
あなたの議論には単一の大きな欠陥があります。「不測の事態」は例外ではなく、ビジネスコードとメソッドの戻り値によって処理する必要があります。例外は、言葉が言うように、例外的な状況、それゆえフォールトのためのものです。
Matteo Mosca、2014

@MatteoMoscaエラーの戻りコードは無視される傾向にあり、それで失格となります。実際、異常なものはスタックのどこかでしか処理できないことがよくありますが、これは例外の使用例です。私はFile#openInputStream戻るようなものを想像することができEither<InputStream, Problem>ます-それがあなたの意味するものであるなら、私たちは同意するかもしれません。
maaartinus

19

要するに:

例外はAPIの設計上の問題です。 -それ以上でもそれ以下でもありません。

チェックされた例外の引数:

なぜチェック例外が良いことではないのかを理解するために、質問を振り返ってみましょう。チェック例外が魅力的である場合と理由、つまりコンパイラに例外宣言を強制する理由は何ですか?

答えは明らかです。例外をキャッチする必要がある場合があります。これは、呼び出されるコードが、関心のあるエラーに対して特定の例外クラスを提供する場合にのみ可能です。

したがって、引数のためのチェック例外は、例外がスローされている宣言するために、コンパイラ軍のプログラマーということで、うまくいけば、プログラマは、また、それらの原因という特定の例外クラスとエラーを文書化します。

ただし実際には、パッケージcom.acmeがスローするのAcmeExceptionは特定のサブクラスではなく、サブクラスである場合が多すぎます。次に、呼び出し元は、処理、宣言、または再シグナルを送信する必要AcmeExceptionsがありますが、AcmeFileNotFoundError発生したのか、それともであるのかはまだわかりませんAcmePermissionDeniedError

したがって、にのみ関心があるAcmeFileNotFoundError場合の解決策は、ACMEプログラマーに機能リクエストを提出し、そののサブクラスを実装、宣言、および文書化するように指示することですAcmeException

なぜわざわざ?

したがって、チェックされた例外があったとしても、コンパイラーはプログラマーに有用な例外をスローするように強制できません。それはまだAPIの品質の問題です。

結果として、チェック例外のない言語は通常、それほど悪くはありません。プログラマーは、Errorではなく一般クラスの非特定のインスタンスをスローするように誘惑されるかもしれませんがAcmeException、APIの品質をまったく気にしない場合、結局のところ紹介することを学びAcmeFileNotFoundErrorます。

全体として、例外の仕様とドキュメントは、たとえば通常のメソッドの仕様とドキュメントとそれほど変わりません。これらもAPIの設計上の問題であり、プログラマーが有用な機能を実装またはエクスポートするのを忘れた場合は、APIを改善して、便利に使用できるようにする必要があります。

この推論に従うと、Javaのような言語で一般的である、例外の宣言、キャッチ、再スローの「煩わしさ」が、ほとんど価値をもたらさないことは明らかです。

また、Java VMは例外をチェックしていませ。Javaコンパイラだけが例外をチェックし、例外宣言が変更されたクラスファイルは実行時に互換性があります。Java VMのセキュリティは、チェックされた例外によっては改善されず、コーディングスタイルのみが改善されます。


4
あなたの議論はそれ自体に反論します。「ときどき例外をキャッチする必要がある」場合、APIの品質が低いことが多く、チェックされた例外がないと、特定のメソッドがキャッチする必要のある例外をスローすることを設計者が文書化しなかったかどうかはわかりません。それを投げるのAcmeExceptionではなく、投げAcmeFileNotFoundErrorて、あなたが間違ったこととどこでそれをキャッチする必要があるかを理解してください。チェック例外は、プログラマーに不正なAPI設計に対するわずかな保護を提供します。
エヴァ

1
Javaライブラリの設計は重大な誤りを犯しました。「チェックされた例外」は、ファイルが見つからない、接続の失敗など、予測可能で回復可能な不測の事態に対するものでした。それらは決して、低レベルのシステム障害には適していませんでした。ファイルを開くことを強制的にチェックすることは問題ありませんでしたが、1バイトの書き込み/ SQLクエリの実行などの失敗に対する賢明な再試行または回復はありません。再試行または回復は、「ビジネス」または「要求」で正しく処理されます"レベル、チェックされた例外は無意味に困難になります。literatejava.com/exceptions/...
トーマス・W

17

私は過去3年間、比較的複雑なアプリケーションで複数の開発者と協力してきました。Checked Exceptionを適切に使用して適切なエラー処理を行うコードベースと、そうでないコードベースがあります。

これまでのところ、Checked Exceptionsを使用してコードベースを操作する方が簡単です。他の誰かのAPIを使用している場合、コードを呼び出してログに記録したり、表示したり、無視したりすることによって、コードを呼び出して適切に処理するときに予期できるエラー状態を正確に確認できるのは素晴らしいことです(はい、無視するのに有効なケースがあります) ClassLoader実装などの例外)。それは私が書いているコードに回復する機会を与えます。すべてのランタイム例外は、それらがキャッシュされ、いくつかの一般的なエラー処理コードで処理されるまで伝播します。特定のレベルで実際に処理したくないチェック例外、またはプログラミングロジックエラーを見つけた場合は、それをRuntimeExceptionにラップしてバブルアップさせます。決して、正当な理由なしに例外を飲み込まないでください(これを行う正当な理由はかなり少ないです)

例外をチェックしていないコードベースで作業する場合、関数を呼び出すときに何を期待できるかを事前に知るのが少し難しくなり、何かがひどく壊れる可能性があります。

もちろん、これは好みと開発者のスキルの問題です。プログラミングとエラー処理の両方の方法は同じように効果的(または非効果的)になる可能性があるため、「一方通行」があるとは言えません。

概して、特に多くの開発者がいる大規模なプロジェクトでは、チェック例外を使用する方が簡単です。


6
そうする。私にとって、それらは契約の重要な部分です。APIのドキュメントで詳しく説明する必要がないため、最も可能性の高いエラーシナリオをすばやく知ることができます。
ウェインハートマン、

2
同意します。ネットワーク呼び出しを行おうとしたときに、.Netでチェック例外の必要性を一度経験しました。ネットワークで一時的な問題が発生する可能性があることを知っていたため、APIのドキュメント全体を読んで、そのシナリオで特にキャッチする必要のある例外を見つける必要がありました。C#が例外をチェックしていたら、すぐにわかりました。他のC#開発者は、おそらく単純なネットワークエラーでアプリをクラッシュさせるだけでしょう。
AxiomaticNexus 14年

13

例外カテゴリ

例外について話すときは、いつもEric LippertのVexing exceptionsブログの記事を参照します。彼は例外を次のカテゴリに分類します。

  • 致命的 -これらの例外はあなたのせいではありません。その場合、それを防ぐことはできず、それらを賢明に処理することはできません。たとえば、OutOfMemoryErrorまたはThreadAbortException
  • 骨の折れる -これらの例外はあなたの責任です:あなたはそれらを防ぐべきでした、そしてそれらはあなたのコードのバグを表しています。たとえばArrayIndexOutOfBoundsExceptionNullPointerExceptionまたはany IllegalArgumentException
  • Vexing-これらの例外は例外ではなく、あなたの責任ではありません。それらを防ぐことはできませんが、対処する必要があります。これらは、解析の失敗時にブール値のfalseを返すメソッドを提供する代わりにNumberFormatExceptionからスローするなど、不幸な設計決定の結果であることがよくあります。Integer.parseIntInteger.tryParseInt
  • 外因性 -これらの例外は通常、例外的であり、障害ではありません。それらを(合理的に)防止することはできませんが、処理する必要があります。たとえば、FileNotFoundException

APIユーザー:

  • 致命的または骨の折れる例外を処理してはなりません
  • 厄介な例外処理する必要がありますが、理想的なAPIでは発生しません。
  • 外因性の例外を処理する必要があります。

チェックされた例外

APIユーザー特定の例外を処理する必要があるという事実は、呼び出し元と呼び出し先の間のメソッドのコントラクトの一部です。コントラクトは、特に、呼び出し先が期待する引数の数とタイプ、呼び出し元が期待できる戻り値のタイプ、および呼び出し元が処理することを期待される例外を指定します。

厄介な例外はAPIに存在してはならないので、メソッドのコントラクトの一部となるには、例外チェックする必要があるのはこれらの外因的な例外だけです。外因性の例外は比較的少ないため、どのAPIでもチェック例外は比較的少ないはずです。

チェック例外は、処理する必要のある例外です。例外の処理は、それを飲み込むのと同じくらい簡単です。そこ!例外が処理されます。限目。開発者がそのように処理したい場合は、問題ありません。しかし、彼は例外を無視することはできず、警告されています。

APIの問題

しかし、厄介致命的な例外をチェックしたAPI (JCLなど)は、APIユーザーに不要な負担をかけます。このような例外は処理する必要がありますが、例外が非常に一般的であるため、そもそも例外であるはずがありませんでした。あるいは、それを処理するときには何もできません。そして、これは、 Java開発者がチェック例外を憎むようになります。

また、多くのAPIには適切な例外クラス階層がないため、あらゆる種類の非外来例外の原因が単一のチェック済み例外クラス(例:)で表されますIOException。また、これによりJava開発者はチェックされた例外を嫌います。

結論

外因性の例外は、ユーザーの責任ではなく、防止できなかった例外であり、処理する必要があります。これらは、スローされる可能性のあるすべての例外の小さなサブセットを形成します。APIは外因性の例外のみをチェックし、他のすべての例外はチェックしないでください。これにより、APIが改善され、APIユーザーへの負担が軽減されるため、すべてをキャッチしたり、チェックされていない例外を飲み込んだり、再スローしたりする必要がなくなります。

したがって、Javaとそのチェックされた例外を嫌わないでください。代わりに、チェックされた例外を多用するAPIは嫌いです。


そして、階層を持たないことでそれらを誤用します。
TofuBeer 2013年

1
FileNotFoundとJDBC /ネットワーク接続の確立は偶発的であり、これらは予測可能で(可能な場合)回復可能であるため、チェック例外は正しいです。他のほとんどのIOExceptions、SQLExceptions、RemoteExceptionなどは、予測不能で回復不可能なエラーであり、ランタイム例外である必要があります。誤ったJavaライブラリの設計のため、私たちは全員この誤りにひどく集中しており、現在は主にSpring&Hibernate(設計を正しく理解している)を使用しています。
トーマスW

「処理」とは呼びたくないかもしれませんが、通常は骨の折れた例外を処理する必要があります。たとえば、Webサーバーでそれらをログに記録し、ユーザーに500を表示します。例外は予想外なので、バグ修正の前にできることはほぼすべてあります。
maaartinus 2017

6

確認された例外は理想的ではなく、いくつかの注意点がありますが、目的を果たします。APIを作成するとき、このAPIの契約である特定の失敗のケースがあります。Javaなどの強く静的に型付けされた言語のコンテキストで、チェックされた例外を使用しない場合、エラーの可能性を伝えるためにアドホックドキュメントと規則に依存する必要があります。そうすることで、コンパイラーがエラー処理にもたらすすべての利点が取り除かれ、プログラマーの善意に完全に委ねられます。

したがって、C#で行われたようなChecked例外を削除すると、どのようにしてプログラムおよび構造的にエラーの可能性を伝えることができますか?このようなエラーが発生する可能性があり、対処する必要があることをクライアントコードに通知する方法

チェックされた例外を処理するとき、あらゆる種類の恐怖が聞こえます。それらは誤用されていますが、これは確かですが、チェックされていない例外も同様です。APIが多くの層に深く積み重ねられて、失敗を伝えるためのある種の構造化された手段の復活が求められるようになると、数年待つと言います。

例外がAPIレイヤーの下部のどこかでスローされ、このエラーが発生する可能性さえないと誰もが認識していないため、バブルを発生させた場合を考えてみてください。それをスローしました(たとえば、VogonsTrashingEarthExcept ...とは対照的にFileNotFoundException...。この場合、処理するものがないため、処理するかどうかは問題ではありません)

多くの人は、ファイルをロードできないことは、ほとんどの場合、そのプロセスの世界の終わりであり、それは恐ろしくて痛い死を遂げなければならないと主張しました。ええ..確かに... ok ..何かのAPIを構築し、ある時点でファイルをロードします...私は、そのAPIのユーザーとして応答することしかできません...「誰がいつ私を決定するのかプログラムがクラッシュするはずです!」確かに、例外が乱されてトレースが残されないか、スタックトレースがマリアンナトレンチよりも深いEletroFlabbingChunkFluxManifoldChuggingExceptionを残すという選択を考えると、迷わずに後者を利用しますが、これは例外に対処するための望ましい方法であることを意味します?例外が新しいレベルの抽象化にトラバースするたびに例外が再キャストおよびラップされ、実際に何かが発生するような、中間のどこかにいることはできませんか?

最後に、私が目にする議論のほとんどは、「例外を処理したくない、多くの人は例外を処理したくない、チェックされた例外は私にそれらを処理することを強いるので、チェックされた例外が嫌いだ」というようなメカニズムを完全に排除し、それをgoto地獄の裂け目に任せるのは愚かなことであり、判断力とビジョンが欠けています。

チェック例外を排除すれば、関数の戻り値の型も排除し、常に「anytype」変数を返すことができます。


2
チェックされた例外は、ブロック内のメソッド呼び出しのどれもがチェックされた例外をスローすることが期待されておらず、そのような例外は自動的にラップされて再スローされる必要があると宣言する手段がある場合に役立ちます。チェック例外をスローするように宣言されたメソッドの呼び出しが、呼び出し速度/戻り値と例外処理速度のトレードオフである場合(期待される例外を通常のプログラムフローとほぼ同じ速さで処理できるようにするため)、これらはさらに便利です。現在、どちらの状況も当てはまりません。
スーパーキャット2014年

6

実際、一方でチェックされた例外は、プログラムの堅牢性と正確さを向上させます(インターフェースの正しい宣言を行うことを強制されます-メソッドがスローする例外は基本的に特別な戻り型です)。一方、例外は「バブルアップ」するため、例外を1つ変更すると、多くの場合、すべてのメソッド(すべての呼び出し元、呼び出し元の呼び出し元など)を変更する必要があるという問題に直面します。メソッドがスローします。

Javaでチェックされた例外は、後者の問題を解決しません。C#とVB.NETは、お風呂の水で赤ちゃんを捨てます。

中道をたどる優れたアプローチは、このOOPSLA 2005のペーパー(または関連するテクニカルレポート)で説明されています。

つまり、次のように言うことができますmethod g(x) throws like f(x)。出来上がり、カスケード変更の問題なしに例外をチェックしました。

学術論文ですが、チェック例外のメリットとデメリットを説明するのに役立ちますので、その一部を読むことをお勧めします。


5

これはチェック例外の純粋な概念に対する反対ではありませんが、Javaが使用するクラス階層は奇抜なショーです。私たちは常に物事を単に「例外」と呼んでいます。これは正しいです。言語仕様でもそう呼んでいるからです。しかし、例外はどのように命名され、型システムで表現されますか?

クラスでException想像できますか?そうでExceptionはありません。s は例外であり、同様に例外はExceptionsです。ただし、sでない Exception例外は例外です。他の例外は実際にはErrorsです。これは、他の種類の例外であり、例外が発生しない例外的な例外です。それがそうであるとき、そしてあなたが時々あなたがしなければならないことを除いて、あなたは決してそれを捕らえるべきではありません。ExceptionsでもErrors でもない単なるThrowable例外である他の例外を定義することもできるため、それだけではありません。

「チェックされた」例外はどれですか?Throwablesが、彼らはまたしている場合を除き、例外をチェックしているError未チェック例外である、SおよびそこだExceptionでもあるS、ThrowableS、及びそれには例外があまりにもあります除いていることがあれば、彼らである、チェック例外の主要なタイプですが、これもRuntimeExceptionsです。これは、他の種類の未チェックの例外だからです。

RuntimeExceptionのために?名前が示すように、それらはすべてExceptionの例外と同様に例外であり、実際にすべての例外と同様に実行時に発生しますが、RuntimeExceptionsは他の実行時Exceptionの例外と比較して例外です。RuntimeExceptionsは決してErrorsとは言えませんが、なんらかの愚かなエラーを犯した場合、それらは例外的に誤っているものの、実際にはErrors ではないもののためのものです。除いてRuntimeErrorException実際にある、RuntimeExceptionのためErrorの。しかし、すべての例外がとにかく誤った状況を表すはずではありませんか?はい、すべてです。を除いてThreadDeath、例外的に例外のない例外です。ドキュメントでは、これが「通常の発生」であり、Error

とにかく、真ん中のすべての例外をErrors(例外的な実行例外のため、チェックされていない)とExceptions(例外的な実行エラーが少ないため、そうでない場合を除いてチェックされる)に分割しているため、2つ必要です。いくつかの例外のそれぞれの種類。だから、我々は必要IllegalAccessErrorIllegalAccessExceptionし、InstantiationErrorそしてInstantiationException、そしてNoSuchFieldErrorそしてNoSuchFieldException、そしてNoSuchMethodErrorそしてNoSuchMethodException、そしてZipErrorそしてZipException

例外がチェックされる場合でも、コンパイラをだましてチェックせずにスローする方法は常に(かなり簡単に)あります。あなたがあなたを行う場合は、あなたが得ることがUndeclaredThrowableException、他の場合を除き、それはどこのように投げることができUnexpectedException、またはUnknownException(無関係にあるUnknownError、または唯一の「重大な例外」のためである、) ExecutionException、またはInvocationTargetException、またはExceptionInInitializerError

ああ、そしてJava 8のおしゃれなnewを忘れてはいけませんUncheckedIOException。これは、I / Oエラーによって発生したRuntimeExceptionチェック済みのIOException例外(IOError存在するものの、例外は発生しません)をラップすることによって、例外チェックの概念をウィンドウから投げ出すように設計された例外です。それも非常に扱いにくいので、チェックしないでください。

Javaに感謝!


2
私の言う限りでは、この答えは「Javaの例外はめちゃくちゃだ」と皮肉たっぷりで間違いなく面白い方法でしか述べていません。プログラマがこれらのものがどのように機能することになっているのかを理解しようとするのを避けがちである理由を説明することはそうではないようです。また、実際のケース(少なくとも私が対処する機会があったケース)では、プログラマーが意図的に生活を困難にしようとしない限り、例外はあなたが説明したほど複雑ではありません。
CptBartender 2016年

5

問題

例外処理メカニズムで私が目にする最悪の問題は、コードの重複が大規模に導入されることです。正直に言いましょう。95%の時間のほとんどのプロジェクトで、開発者が例外を除いて実際に行う必要があるのは、なんらかの方法でユーザー(および場合によっては開発チームにも)に伝えることです。たとえば、e -スタックトレース付きのメール)。したがって、通常、例外が処理されるすべての場所で同じコード/コードブロックが使用されます。

いくつかのタイプのチェック例外について、各catchブロックで単純なロギングを行うと仮定します。

try{
   methodDeclaringCheckedException();
}catch(CheckedException e){
   logger.error(e);
}

それが一般的な例外である場合、より大きなコードベースに数百ものそのようなtry-catchブロックが存在する可能性があります。ここで、コンソールログの代わりにポップアップダイアログベースの例外処理を導入するか、開発チームに電子メールを追加送信する必要があると仮定します。

ちょっと待って...コード内の数百の場所すべてを本当に編集しますか?!あなたは私のポイントを得る:-)。

ソリューション

この問題に対処するために行ったのは、例外処理を集中化するために、例外ハンドラーの概念(これをさらにEHと呼びます)を導入することでした。例外を処理する必要があるすべてのクラスに、Dependency Injectionフレームワークによって例外ハンドラのインスタンスが注入されます。したがって、例外処理の一般的なパターンは次のようになります。

try{
    methodDeclaringCheckedException();
}catch(CheckedException e){
    exceptionHandler.handleError(e);
}

例外処理をカスタマイズするには、コードを1か所(EHコード)で変更するだけです。

もちろん、より複雑なケースでは、EHのいくつかのサブクラスを実装し、DIフレームワークが提供する機能を活用できます。DIフレームワークの構成を変更することで、EH実装をグローバルに簡単に切り替えたり、EHの特定の実装を特別な例外処理が必要なクラスに提供したりできます(たとえば、Guice @Namedアノテーションを使用)。

このようにして、アプリケーションの開発バージョンとリリースバージョンでの例外処理動作を区別できます(例:開発-エラーのログ記録とアプリケーションの停止、製品-詳細なエラーのログ記録とアプリケーションの実行の継続)。

最後に

最後に重要なことですが、例外がいくつかの最上位の例外処理クラスに到達するまで例外を「上に」渡すだけで、同じ種類の集中化を実現できるように思えるかもしれません。しかし、それはコードの散らかりとメソッドのシグネチャの混乱につながり、このスレッドで他の人が言及するメンテナンスの問題を引き起こします。


6
例外は、何か便利なことをするために考案されました。それらをログファイルに書き込んだり、きれいなウィンドウをレンダリングしたりしても、元の問題が解決されないため、役に立ちません。役立つことを行うには、別のソリューション戦略を試す必要があります。例:サーバーAIからデータを取得できない場合は、サーバーBで試してください。または、アルゴリズムAがヒープオーバーフローを生成する場合は、アルゴリズムBを試してください。
2013年

2
@cevingええ、それは理論的にはすべて善良で真実です。しかし、今度は単語の練習に戻りましょう。実際のプロジェクトでどれくらいの頻度で正直に答えてください。catchこの実際のプロジェクトのブロックのどの部分が、exceptinsで本当に「有用」なことをしますか?10%が良いでしょう。例外を生成する通常の問題は、存在しないファイル、OutOfMemoryErrors、NullPointerExceptions、データベース制約整合性エラーなどから設定を読み取ろうとするようなものです。本当にそれらすべてから正常に回復しようとしていますか?私はあなたを信じていません:)。多くの場合、回復する方法はありません。
Piotr Sobczyk 2013年

3
@PiotrSobczyk:suer要求の結果としてプログラムが何らかのアクションを実行し、操作が何らかの形で失敗し、システム状態に何も損傷していない場合、操作を完了できなかったことをユーザーに通知することは完全に便利です状況を処理する方法。C#と.netでの例外の最大の失敗は、システム状態の何かが破損したかどうかを確認する一貫した方法がないことです。
スーパーキャット2013年

4
正解です、@ PiotrSobczyk。ほとんどの場合、例外に応答して行う唯一の正しいアクションは、トランザクションをロールバックしてエラー応答を返すことです。「例外を解決する」という考えは、私たちが持っていない(すべきではない)知識と権限を暗示し、カプセル化に違反しています。アプリケーションがDBでない場合は、DBを修正するべきではありません。クリーンに失敗し、誤ったデータの書き込みを回避することは、cevingで十分です。
トーマスW

@PiotrSobczyk昨日、「オブジェクトを読み取れませんでした」例外(これは、基礎となるデータベースがソフトウェアの前に更新されたためにのみ発生します-発生することはありませんが、人為的エラーが原因である可能性があります)にフェイルオーバーして対処しました古いバージョンのオブジェクトを指すことが保証されているデータベースの履歴バージョン。
2014年


4

私のc2.comへの書き込みは、ほとんどが元の形式から変更されていません。 CheckedExceptionsAreIncompatibleWithVisitorPattern

要約すれば:

訪問者パターンとその関連要素は、間接的な呼び出し元とインターフェイスの実装の両方が例外について認識しているが、インターフェイスと直接の呼び出し元が認識できないライブラリを形成しているインターフェイスのクラスです。

CheckedExceptionsの基本的な前提は、宣言されたすべての例外は、その宣言でメソッドを呼び出す任意のポイントからスローできることです。VisitorPatternは、この仮定に誤りがあることを明らかにしています。

このような場合のチェック例外の最終結果は、実行時にコンパイラのチェック例外制約を本質的に削除する、それ以外の場合は役に立たない多くのコードです。

根本的な問題については:

私の一般的な考えは、トップレベルのハンドラーが例外を解釈して適切なエラーメッセージを表示する必要があるということです。ほとんどの場合、IO例外、通信例外(何らかの理由でAPIが区別する)、またはタスク致命的なエラー(プログラムのバグまたはバッキングサーバーでの深刻な問題)のいずれかが表示されるため、深刻なスタックトレースを許可する場合、これはそれほど難しくありませんサーバーの問題。


1
インターフェイスにDAGNodeExceptionのようなものが必要です。次に、IOExceptionをキャッチして、DAGNodeExceptionに変換します。public void call(DAGNode arg)throws DAGNodeException;
TofuBeer 2009年

2
@TofuBeer、それがまさに私のポイントです。例外を常にラップおよびアンラップすることは、チェックされた例外を削除するよりも悪いことに気づきました。
ジョシュア

2
そうですね、完全に同意するわけではありません...しかし、ランタイム例外がスローされたときにアプリケーションがスタックトレースをユーザーに表示しないようにする方法についての根本的な質問には、記事はまだ答えていません。
TofuBeer 2009年

1
@TofuBeer-失敗したときに、失敗したことをユーザーに伝えるのは正しいことです!「null」または不完全な/不正確なデータでの失敗を「ペーパーオーバー」する以外に、代替手段は何ですか?それが成功したふりは嘘であり、それは物事をさらに悪化させるだけです。高信頼性システムで25年の経験があるため、再試行ロジックは、適切かつ適切な場合にのみ使用する必要があります。また、何度再試行しても、Visitorが再び失敗する可能性が高いと思います。飛行機を飛ばしていない限り、同じアルゴリズムの2番目のバージョンに交換することは非現実的であり、妥当ではありません(とにかく失敗する可能性があります)。
Thomas W

4

未回答の質問のみに対処するには:

Exceptionサブクラスの代わりにRuntimeExceptionサブクラスをスローする場合、何をキャッチすることになっているのかをどのようにして知るのですか?

質問には、疑わしい推論が含まれています。APIがスローするものをAPIが指示するからといって、すべてのケースで同じように処理するわけではありません。言い換えると、キャッチする必要がある例外は、例外をスローするコンポーネントを使用するコンテキストによって異なります。

例えば:

データベースの接続テスターや、ユーザーが入力したXPathの有効性を確認するための何かを作成している場合は、操作によってスローされたすべてのチェック済みおよび未チェックの例外をキャッチして報告する必要があります。

ただし、処理エンジンを作成している場合は、XPathException(チェック済み)をNPEと同じ方法で処理する可能性があります。ワーカースレッドの先頭まで実行させ、残りのバッチをスキップしてログに記録します問題(または診断のためにサポート部門に送信)し、ユーザーがサポートに連絡するためのフィードバックを残します。


2
丁度。簡単でわかりやすい、例外処理の方法。デイブが言うように、正しい例外処理は通常高レベルで行われます。「早く投げ、遅くキャッチ」が原則です。チェック例外はそれを困難にします。
トーマスW

4

この記事は、私が今まで読んだJavaの例外処理に関する最高のテキストです。

チェックされた例外よりもチェックされていない例外を優先しますが、この選択は非常に十分に説明され、強力な議論に基づいています。

ここでは記事の内容の多くを引用したくありません(全体として読むことをお勧めします)が、このスレッドからのチェックされていない例外の擁護者のほとんどの引数をカバーしています。特にこの議論(これは非常に人気があるようです)がカバーされています:

例外がAPIレイヤーの下部のどこかでスローされ、このエラーが発生する可能性さえないと誰もが認識していないため、バブルを発生させた場合を考えてみてください。それをスローしました(たとえば、VogonsTrashingEarthExcept ...とは対照的にFileNotFoundException...。この場合、処理するものがないため、処理するかどうかは問題ではありません)

著者の「対応」:

すべての実行時例外をキャッチして、アプリケーションの「最上部」に伝播することを許可するべきではないと仮定することは絶対に誤りです。(...)システム/ビジネス要件によって明確に処理する必要があるすべての例外条件について、プログラマはそれをどこでキャッチするか、そして条件がキャッチされたら何をするかを決定する必要があります。これは、コンパイラのアラートに基づくのではなく、アプリケーションの実際のニーズに厳密に従って行う必要があります。他のすべてのエラーは、ログに記録される最上位のハンドラーに自由に伝播できるようにする必要があり、適切な(おそらく終了)アクションが実行されます。

そして主な考えや記事は:

ソフトウェアでのエラー処理に関して言えば、存在する可能性のある唯一の安全で正しい仮定は、存在するすべてのサブルーチンまたはモジュールで障害が発生する可能性があるということだけです。

したがって、「このエラーが発生する可能性さえあると誰もが知らなかった場合、そのプロジェクトに問題があります。そのような例外は、著者が示唆するように、少なくとも最も一般的な例外ハンドラー(より具体的なハンドラーで処理されないすべての例外を処理するものなど)で処理する必要があります。

とても悲しいので、多くの人がこの素晴らしい記事を発見するようではないようです:-(。


4

チェックされた例外は、元の形式では、障害ではなく不測の事態を処理する試みでした。称賛に値する目標は、特定の予測可能なポイント(接続できない、ファイルが見つからないなど)を強調し、開発者がこれらを確実に処理することでした。

元のコンセプトに含まれていなかったのは、広範囲にわたる体系的で回復不可能な障害を強制的に宣言することでした。これらの失敗は、チェックされた例外として宣言されるために正しいものではありませんでした。

コードで障害が発生する可能性があり、EJB、Web、Swing / AWTコンテナは、最も外側の「失敗した要求」例外ハンドラを提供することで、これに対応しています。最も基本的な正しい戦略は、トランザクションをロールバックしてエラーを返すことです。

重要な点の1つは、ランタイム例外とチェック済み例外は機能的に同等であることです。チェックされた例外が実行できる処理や回復はありませんが、ランタイム例外は実行できません。

「チェック済み」例外に対する最大の議論は、ほとんどの例外は修正できないということです。単純な事実は、壊れたコード/サブシステムを所有していないということです。私たちは実装を見ることができず、私たちはそれに対して責任がなく、修正することはできません。

アプリケーションがDBではない場合、DBを修正するべきではありません。これはカプセル化原則に違反し ます

特に問題となっているのは、JDBC(SQLException)とRMI for EJB(RemoteException)の領域です。元の「チェックされた例外」の概念に従って修正可能な不測の事態を特定するのではなく、これらの強制された広範なシステム全体の信頼性の問題は実際には修正可能ではなく、広く宣言されます。

Java設計のもう1つの重大な欠陥は、例外処理が可能な限り最高の「ビジネス」または「要求」レベルに正しく配置されることでした。ここでの原則は、「早く投げ、遅くキャッチ」です。チェックされた例外はほとんど機能しませんが、この方法で邪魔になります。

何千もの何もしないtry-catchブロックを必要とするというJavaの明らかな問題があり、かなりの割合(40%以上)が誤ってコーディングされています。これらのほとんどは、本物の処理や信頼性を実装していませんが、主要なコーディングのオーバーヘッドを課しています。

最後に、「チェック済み例外」は、FP関数型プログラミングとほとんど互換性がありません。

「すぐに処理する」に対する彼らの主張は、「遅れてキャッチする」例外処理のベストプラクティスと、ループや制御のフローを抽象化するFP構造の両方と対立しています。

多くの人がチェックされた例外の「取り扱い」について話しますが、彼らの帽子を通して話しています。失敗の後にnull、不完全、または正しくないデータで成功を装って続行しても、何も処理されません。それは、最も低い形式のエンジニアリング/信頼性の不正行為です。

きれいに失敗することは、例外を処理するための最も基本的な正しい戦略です。トランザクションをロールバックし、エラーをログに記録し、ユーザーに「失敗」の応答を報告することは、適切な方法です。最も重要なのは、誤ったビジネスデータがデータベースにコミットされないようにすることです。

例外処理の他の戦略は、ビジネス、サブシステム、または要求レベルでの「再試行」、「再接続」、または「スキップ」です。これらはすべて、一般的な信頼性戦略であり、実行時の例外でうまく機能します。

最後に、不正なデータで実行するよりも、失敗する方がはるかに望ましいです。続行すると、二次的なエラーが発生し、元の原因から離れ、デバッグが難しくなります。または、最終的に誤ったデータがコミットされます。人々はそのために解雇されます。

参照:
- http://literatejava.com/exceptions/checked-exceptions-javas-biggest-mistake/


1
私のポイントは、一般的な戦略として適切失敗することです。チェックされていない例外は、catchブロックの挿入を強制しないので役立ちます。キャッチとエラーログは、コードベース全体で数千回誤ってコーディングされた(実際にはバグを隠している)のではなく、いくつかの最も外側のハンドラーに任せることができます。任意の失敗については、チェックされていない例外が絶対的に最も適切です。不測の事態 -資金不足などの予測可能な結果-は、正当に確認する価値がある唯一の例外です。
トーマスW

1
上記の私の回答はこれに対処しています。何よりもまず、1)最も外側の障害ハンドラーがすべてをキャッチする必要があります。それを超えて、特定された特定のサイトに対してのみ、2)特定の予期された不測の事態が検出され、処理されます-すぐにサイトでスローされます。つまり、ファイルが見つからない、資金が不足している、などの理由で、これらのファイルが回収される可能性があります。カプセル化の原則は、外側の層が内部の障害を理解したり、障害から回復したりする責任を負わない/責任を負わないことを意味します。3番目に、3)それ以外はすべて外側に向けてスローする必要があります-可能であればチェックを外します。
トーマスW

1
最も外側のハンドラーが例外をキャッチしてログに記録し、「失敗した」応答を返すか、エラーダイアログを表示します。非常にシンプルで、定義するのは難しいことではありません。重要なのは、すべての例外がすぐにローカルで回復できるわけではなく、カプセル化の原則による回復不可能なエラーであるということです。知ることを意図したコードがそれを回復できない場合、リクエストは全体的にクリーンかつ適切に失敗します。これは適切に行うための正しい方法です。
トーマスW

1
不正解です。最も外側のハンドラーの仕事は、きれい失敗し、「リクエスト」境界でエラーログに記録することです。壊れた要求は適切に失敗し、例外が報告され、スレッドは次の要求の処理を続行できます。このような最も外側のハンドラーは、Tomcat、AWT、Spring、EJBコンテナー、およびJavaの「メイン」スレッドの標準機能です。
トーマスW

1
リクエストの境界または最も外側のハンドラで「本物のバグ」を報告することが危険なのはなぜですか?私はシステムの統合と信頼性で頻繁に働いていますが、正しい信頼性エンジニアリングが実際に重要であり、「チェックされていない例外」アプローチを使用してそうしています。私はあなたが実際に何を議論しているのか本当にわかりません-あなたは実際に未チェックの例外の方法で3か月を費やして、それを感じて、そしておそらく私たちがさらに議論することができるように思えるかもしれません。ありがとう。
トーマスW

4

すでに述べたように、Javaバイトコードにはチェック済み例外は存在しません。これらは単なるコンパイラメカニズムであり、他の構文チェックとは異なります。コンパイラが冗長な条件について不平を言っているのを見るのと同じように、チェック例外が表示されますif(true) { a; } b;。それは役に立ちますが、私は故意にこれを行ったかもしれないので、警告を無視します。

問題の事実は、チェックされた例外を適用し、他のすべての人があなたが作ったルールを嫌っているだけの副次的損害になっている場合、すべてのプログラマーに「正しいこと」を強制することができないということです。

そこに悪いプログラムを修正してください!それらを許可しないように言語を修正しようとしないでください!ほとんどの人にとって、「例外について何かをする」とは、本当にそれをユーザーに伝えることです。チェックされていない例外についてもユーザーに伝えることができるので、チェックされた例外クラスをAPIから除外します。


右、到達できないコード(エラーを生成する)と予測可能な結果を​​伴う条件文の違いを強調したかっただけです。このコメントは後で削除します。
Holger

3

チェックされた例外の問題は、そのインターフェイスの1つの実装がそれを使用する場合でも、例外がインターフェイスのメソッドにアタッチされることが多いことです。

チェック例外のもう1つの問題は、それらが誤用される傾向があることです。この完璧な例は、java.sql.Connectionclose()メソッドです。Connectionの処理が完了したことを明示的に述べSQLExceptionいても、それはをスローする可能性があります。close()があなたが気にしていることを伝える情報は何でしょうか?

通常、接続をclose()すると*、次のようになります。

try {
    conn.close();
} catch (SQLException ex) {
    // Do nothing
}

また、さまざまな解析メソッドとNumberFormatExceptionを開始しないでください。例外をスローしない.NETのTryParseは非常に使いやすく、Javaに戻る必要があるので大変です(Javaと私が働いているC#)。

*追加のコメントとして、PooledConnectionのConnection.close()は接続を閉じることさえしませんが、チェックされた例外であるため、SQLExceptionをキャッチする必要があります。


2
そうですね、どのドライバーでもできます...問題はむしろ「なぜプログラマーが気にする必要があるのか​​」です。とにかく、彼はデータベースへのアクセスを完了しました。ドキュメントは、close()を呼び出す前に常に現在のトランザクションをcommit()またはrollback()する必要があることを警告しています。
パワーロード2009年

多くの人は、ファイルを閉じても例外をスローすることはできないと考えています... stackoverflow.com/questions/588546/…それが問題になるケースがないことを100%確信していますか?
TofuBeer 2009年

それ問題になることはなく、呼び出し側 try / catch を行わないことは100%確実です。
マーティン

1
マーティン!私はあなたの言い直ししかできません:接続が完了したことを明示的に述べただけで、それを閉じているときに何が起こっているのか気になるはずです。プログラマーが例外が発生しても本当に気にせず、彼がそれについて完全に正しい、このようなケースがさらにあります。
Piotr Sobczyk

1
@PiotrSobczyk:一部のSQLドライバーは、トランザクションの開始後に接続を閉じたが、確認もロールバックもしない場合、スコークします。私見、スコーキングは、少なくともスコーキングによって他の例外が失われない場合には、問題を黙って無視するよりも優れています。
スーパーキャット2014年

3

プログラマは、メソッドを正しく使用するために、メソッドがスローする可能性のあるすべての例外を知る必要があります。そのため、例外のいくつかだけで頭を倒しても、不注意なプログラマがエラーを回避するのに必ずしも役立つとは限りません。

ほんのわずかな利点は、負担のかかるコストよりも重要です(特に、インターフェイスのシグネチャを常に変更することが実用的でない、大きくて柔軟性に欠けるコードベースでは)。

静的分析は優れている場合がありますが、真に信頼できる静的分析は、多くの場合、プログラマーに厳密な作業を柔軟に要求します。費用対効果の計算があり、コンパイル時間エラーにつながるチェックのためにバーを高く設定する必要があります。IDEが、メソッドがスローする可能性のある例外(不可避なものも含む)を通知する役割を引き受ければ、さらに役立ちます。強制的な例外宣言がなければ、信頼性は低くなるかもしれませんが、ほとんどの例外はドキュメントで宣言されているため、IDE警告の信頼性はそれほど重要ではありません。


2

これは素晴らしい質問であり、決して議論の余地はないと思います。サードパーティのライブラリは(一般的に)未チェックの例外をスローするべきだと思います。これは、ライブラリへの依存関係を分離できることを意味します(つまり、例外を再スローしたりスローしたりする必要はありませんException-通常は悪い習慣です)。SpringのDAOレイヤーは、この良い例です。

彼らができれば一方、コアのJava APIから例外が一般的ででチェックする必要があり、これまでに処理すること。FileNotFoundExceptionまたは(私のお気に入り)を取るInterruptedException。これらの条件は、ほとんどの場合、具体的に処理する必要があります(つまり、に対する反応InterruptedExceptionは、に対する反応と同じではありませんIllegalArgumentException)。例外がチェックされるという事実により、開発者は条件が処理可能かどうかを考える必要があります。(とは言っても、InterruptedException適切に処理されたことはほとんどありません!)

もう1つ-a RuntimeExceptionは常に「開発者が何か間違ったことをした」とは限りません。enumusing valueOfを作成しようとすると、不正な引数の例外がスローされenum、その名前はありません。これは必ずしも開発者の間違いではありません。


1
はい、それは開発者による間違いです。彼らは明らかに正しい名前を使用していないため、戻ってコードを修正する必要があります。
AxiomaticNexus 14年

@AxiomaticNexus正気な開発者はenumenum代わりにオブジェクトを使用するという理由だけでメンバー名を使用しません。したがって、間違った名前は、インポートファイルなど、外部からのみ取得できます。そのような名前を扱う1つの可能な方法はMyEnum#valueOf、IAEを呼び出してキャッチすることです。もう1つの方法は、事前入力されたを使用することですMap<String, MyEnum>が、これらは実装の詳細です。
maaartinus 2017

@maaartinus外部からの文字列なしでenumメンバー名が使用される場合があります。たとえば、すべてのメンバーを動的にループして、それぞれを処理する場合などです。さらに、文字列が外部から来ているかどうかは関係ありません。開発者は、x文字列を "MyEnum#valueOf"に渡すとエラーになるかどうかを知る前に、渡す必要があるすべての情報を持っています。それがエラーを引き起こしたであろうときにとにかくx文字列を "MyEnum#valueOf"に渡すことは、明らかに開発者側の間違いでしょう。
AxiomaticNexus 2017

2

ここさん(joelonsoftware.comから)チェック例外に対する一つの引数に:

推論は、例外はコードのあるポイントから別のポイントに突然ジャンプするという点で、1960年代以降有害と考えられている「gotoの例外」と同じだと私は考えています。実際、それらはgotoのものよりも著しく悪いです:

  • それらはソースコードでは見えません。例外をスローする場合とスローしない場合がある関数を含むコードのブロックを見ると、どの例外がどこからスローされるかを確認する方法はありません。これは、注意深いコード検査でさえ潜在的なバグを明らかにしないことを意味します。
  • 関数の作成可能な出口点が多すぎます。正しいコードを書くためには、関数を通るすべての可能なコードパスについて本当に考える必要があります。例外を発生させてその場でキャッチしない関数を呼び出すたびに、関数が突然終了し、データが一貫性のない状態になるか、その他のコードパスがなかったために、突然のバグが発生する可能性があります。について考える。

3
+1あなたはあなたの答えの中で議論を要約したいかもしれませんか?これらは、プログラム全体に散在する、目に見えないゴトやルーチンの早期終了のようなものです。
MarkJ 2009年

13
それは一般的に例外に対する反対論です。
Ionuț G. Stan

10
あなたは実際に記事を読みましたか?最初に彼は例外全般について語り、次に「ソースコードでは非表示」というセクションは特にUNCHECKED例外に適用されます。これがチェック例外の全体のポイントです...そのため、どのコードがどこでどのコードをスローするかを知ることができます
ニュートピア

2
@Eva彼らは同じではありません。gotoステートメントを使用すると、gotoキーワードを確認できます。ループを使用すると、右中括弧またはbreakor continueキーワードを確認できます。それらのすべては、現在のメソッドのポイントにジャンプします。しかし、あなたはいつでも見ることができないthrowことが多いことが、現在の方法ではなく、それが呼び出すことを別の方法ではないので、(おそらく間接的に。)
finnw

5
@finnw関数自体はgotoの形式です。通常、呼び出す関数がどの関数を呼び出しているかはわかりません。関数なしでプログラミングした場合、目に見えない例外に問題はありません。つまり、問題は例外に特に結びついておらず、一般的に例外に対する有効な議論ではありません。エラーコードの方が速い、モナドのほうがきれいだと言えますが、goto引数はばかげています。
エヴァ

2

Checked Exceptionが不要であることを証明するのは次のとおりです。

  1. Javaで機能する多くのフレームワーク。JDBC例外を未チェックの例外にラップし、ログにメッセージをスローするSpringのように
  2. Javaプラットフォームの最上位であっても、Javaの後に登場した多くの言語-彼らはそれらを使用していません
  3. チェックされた例外は、クライアントが例外をスローするコードをどのように使用するかについての予測です。しかし、このコードを作成する開発者は、コードのクライアントが動作しているシステムとビジネスについて決して知ることはありません。例として、チェックされた例外をスローするように強制するInterfcaceメソッド。システムには100の実装があり、50または90の実装でもこの例外をスローしませんが、ユーザーがそのインターフェイスへのユーザー参照を参照する場合、クライアントはこの例外をキャッチする必要があります。これらの50または90の実装は、それらの例外を自分自身で処理する傾向があり、例外をログに記録します(これは、それらにとって適切な動作です)。それで何をすべきか?私はそのすべての仕事をするいくつかのバックグラウンドロジック-ログにメッセージを送信するほうがよいでしょう。そして、コードのクライアントとして、例外を処理する必要があると感じたら、それを行います。
  4. JavaでI / Oを使用している別の例では、ファイルが存在しない場合、すべての例外をチェックする必要がありますか?それで何をすべきか?存在しない場合、システムは次のステップに進みません。このメソッドのクライアントは、そのファイルから予期されるコンテンツを取得できません-彼はランタイム例外を処理できます。それ以外の場合は、まずチェック例外をチェックし、メッセージをログに記録してから、メソッドから例外をスローします。いいえ...いいえ-私はRuntimeEceptionで自動的にそれを実行する方がよいでしょう。手動で処理する意味はありません。ログにエラーメッセージが表示されたらうれしいです(AOPはそれを助けることができます。Javaを修正するものです)。最終的に、システムがポップアップメッセージをエンドユーザーに表示する必要があると判断した場合、問題ではなく表示します。

I / Oのようなコアライブラリで作業するときに、Javaが何を使用するかの選択を提供してくれると嬉しかったです。Likeは同じクラスの2つのコピーを提供します-1つはRuntimeEceptionでラップされています。次に、人々が使用するものを比較できます。しかし今のところ、多くの人はjavaや別の言語の上にあるフレームワークを使うほうがよいでしょう。Scalaのように、JRubyは何でも。多くの人は、SUNが正しかったと信じています。


1
クラスの2つのバージョンを持つのではなく、コードのブロックによって行われるメソッド呼び出しのどれも特定のタイプの例外をスローすることを期待されていないことを指定する簡潔な方法があるべきであり、そのような例外は特定の方法でラップされるべきであり、再スローされます(デフォルトでRuntimeExceptionは、適切な内部例外を使用して新しいを作成します)。throws後者のアクションコースの方が正しい場合が多いのですが、内部メソッドからの例外をラップするよりも、外部メソッドを内部メソッドからの例外にする方が簡潔であることは残念です。
スーパーキャット2013年

2

誰も言及していない重要なことの1つは、それがインターフェイスやラムダ式にどのように干渉するかです。

を定義するとしますMyAppException extends Exception。これは、アプリケーションによってスローされたすべての例外によって継承される最上位の例外です。すべてのメソッドが宣言しますthrows MyAppExceptionも、どちらが少し厄介であるかをしますが、管理可能です。例外ハンドラーは例外をログに記録し、ユーザーに何らかの方法で通知します。

自分のものではないインターフェイスを実装するまでは、すべて問題ありません。明らかにそれは投げる意図を宣言していませんMyApExceptionため、コンパイラーはそこから例外をスローすることを許可していません。

ただし、例外が拡張された場合はRuntimeException、インターフェースに問題はありません。必要に応じて、JavaDocで自発的に例外に言及できます。しかし、それ以外は何もせず静かにバブルして、例外処理レイヤーでキャッチされます。


1

C#のチーフアーキテクトへの言及がいくつかあります。

チェック例外をいつ使用するかについて、Javaの人から見た別の視点を示します。彼は他の人が言及した否定的な点の多くを認めています: 効果的な例外


2
Javaでのチェック済み例外の問題は、より深い問題から生じます。つまり、インスタンスのプロパティではなく、例外のTYPEに過剰な情報がカプセル化されます。「チェック」がスロー/キャッチサイトの属性であり、コードブロックをエスケープするチェック済み例外をチェック済み例外として残すか、または表示するかを宣言的に指定できる場合は、チェック済み例外があると便利です。チェックされていない例外としての囲みブロック。同様に、catchブロックは、チェックされた例外のみが必要であることを指定できる必要があります。
スーパーキャット2012

1
存在しないキーにアクセスしようとした場合に、特定のタイプの例外をスローするように辞書検索ルーチンが指定されているとします。クライアントコードがこのような例外をキャッチすることは合理的かもしれません。ただし、ルックアップルーチンが使用するいくつかのメソッドが、ルックアップルーチンが予期しない方法で同じタイプの例外をスローした場合、クライアントコードはそれをキャッチしないはずです。チェック状態が例外インスタンスのプロパティであり、サイトをスローし、サイトをキャッチすることで、このような問題を回避できます。クライアントはそのタイプの「チェック済み」例外をキャッチし、予期しない例外を回避します。
スーパーキャット2012

0

(ほとんどの場合)チェック例外の存在に本当に満足または悲しいとは言えない場合でも、例外処理についてたくさん読んだことがあります。これが私の見解です。低レベルコード(IO、ネットワーキングのチェック例外) 、OSなど)および高レベルAPI /アプリケーションレベルでの未チェックの例外。

それらの間に線を引くのがそれほど簡単ではない場合でも、複数のAPI /ライブラリを同じ屋根の下で統合することは本当に煩わしい/困難であることがわかります。いくつかの例外をキャッチし、現在のコンテキストでより意味のある別の例外を提供することを強制するのに便利/優れています。

私が取り組んでいるプロジェクトは、たくさんのライブラリを取り、それらを同じAPIの下で統合します。これは、完全に未チェックの例外に基づいています。このフレームワークは、最初はチェックされた例外でいっぱいで、いくつかのチェックされていない高レベルのAPIを提供します例外(初期化例外、設定例外など)と私はとてもフレンドリーではなかったと言わなければなりません。ほとんどの場合、処理方法がわからない、または気にしない例外をキャッチまたは再スローする必要がありました(特に、例外を無視する必要があります)。クリックすると、10の可能な(チェックされた)例外がスローされる可能性があります。

現在のバージョン(3番目)は、チェックされていない例外のみを使用し、キャッチされなかったものを処理するグローバル例外ハンドラーを備えています。APIは、例外ハンドラーを登録する方法を提供します。これは、例外がエラーと見なされるかどうかを判断します(ほとんどの場合、これが当てはまります)。これは、誰かをログに記録して通知することを意味します。または、この例外、AbortException、これは、現在の実行スレッドを中断し、エラーをログに記録しないことを意味します。もちろん、すべてのカスタムスレッドが機能するためには、run()メソッドをtry {...} catch(all)で処理する必要があります。

public void run(){

try {
     ... do something ...
} catch (Throwable throwable) {
     ApplicationContext.getExceptionService().handleException("Handle this exception", throwable);
}

}

WorkerServiceを使用してすべてを処理するジョブ(Runnable、Callable、Worker)をスケジュールする場合、これは必要ありません。

もちろん、これは私の意見であり、正しいものではないかもしれませんが、私には良いアプローチのように見えます。プロジェクトをリリースした後で、自分にとって良いと思うものが他の人にとっても良いと思うなら、それを見ていきます... :)

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.