Javaでは、チェック例外は何に適していますか?[閉まっている]


14

Javaのチェックされた例外は、長年にわたっていくつかの悪い報道を受けています。わかりやすい兆候は、それが文字通り世界で唯一の言語であることです(GroovyやScalaのような他のJVM言語でさえも)。SpringやHibernateなどの著名なJavaライブラリもそれらを使用しません。

私は個人的に(レイヤー間のビジネスロジックで)それらの1つの用途を見つけましたが、そうでなければ私はかなりアンチチェックされた例外です。

私が気付いていない他の用途はありますか?


すべてのJavaコアライブラリは、チェック例外を非常に広範囲に使用します。
ジョナスプラッカ

3
@Joonas私が知っている、それは苦痛です!
ブラッドキューピット

回答:


22

まず第一に、他のプログラミングパラダイムと同様に、適切に機能するために適切に行う必要があります。

以下のためにがチェック例外の利点は、Javaランタイムライブラリの作者はすでに私が合理的に処理することができると期待されるかもしれない一般的などのような問題は私のために決めたということです呼び出しポイント(トップレベルのキャッチプリントとは対照的に、ダイブロック)およびこれらの問題の処理方法をできるだけ早く検討します。

チェック例外は、エラー回復をできるだけ早く考えさせることでコードをより堅牢にするため、私はチェック例外が好きです。

より正確には、私に基づいて、それは非常に早いと言ってとは対照的に、プロセス内の奇妙なコーナーケースを検討するために私を強制的に、これは、より堅牢な私のコードを作る「ファイルがまだ存在していない場合おっと、私のコードは処理されません」本番環境でエラーが発生した場合、コードを修正して処理する必要があります。既存のコードにエラー処理を追加することは、メンテナンスを開始するときとは対照的に、最初からすぐに行うのではなく、重要なタスクになる可能性があり、したがって費用がかかります。

これは、不足しているファイルが致命的なものであるということかもしれないとプログラムが炎にクラッシュするようになりますが、その後、あなたがしてその決定を下します

} catch (FileNotFoundException e) {
  throw new RuntimeException("Important file not present", e);
}

これは、非常に重要な副作用も示しています。例外をラップする場合は、スタックトレースに説明を追加できます。これは非常に強力です。たとえば、欠落していたファイルの名前、このメソッドに渡されたパラメーター、またはその他の診断情報に関する情報を追加でき、その情報は頻繁にスタックトレースに存在するためです。プログラムがクラッシュしたときに取得します。

人々は「これをデバッガで実行するだけで再現できる」と言うかもしれませんが、私は非常に頻繁に生産エラー後で再現できず、本質的にあなたの仕事がかかっている非常に厄介な場合を除き、生産でデバッガを実行できないことを発見しました。

スタックトレースの情報が多いほど良いです。チェック済みの例外は、その情報を早期に取得するのに役立ちます。


編集:これはライブラリデザイナーにも当てはまります。私が日常的に使用しているライブラリの1つには、多くの多くのチェック済み例外が含まれており、それらを使用することで面倒さが少なくなるように設計することができます。


+1。Springのデザイナーは、多くのHibernateの例外を未チェックの例外に変換するという優れた仕事をしました。
フィル

参考までに、Frank Sommersは、このスレッドで、フォールトトレラントソフトウェアは多くの場合、まったく同じメカニズムに依存していると述べました。おそらく、答えを回復可能なケースと致命的なケースに細分することができます。
-rwong

@rwong、あなたが考えていることをもっと詳しく説明してもらえますか?

11

チェック例外が実際にどのようになったかを説明する2つの良い答えがあります。(両方に+1。)しかし、意図は実際に価値があるので、理論的に彼らが意図したものを調べることも価値があります。

チェックされた例外は、実際には言語の型安全性を高めることを目的としています。整数乗算のような簡単な方法を考えてください。このメソッドの結果タイプは整数であると考えるかもしれませんが、厳密に言えば、結果は整数例外またはオーバーフロー例外です。メソッドの戻り値の型として整数の結果を単独で考慮すると、関数の全範囲が表現されません。

この観点から見ると、チェックされた例外が他の言語への道を見つけられなかったと言うのは厳密には真実ではありません。Javaが使用した形式をとっていません。Haskellアプリケーションでは、代数的データ型を使用して、関数の正常な完了と失敗した完了を区別するのが一般的です。これは例外ではありませんが、それ自体、意図はチェック例外とほとんど同じです。これは、APIコンシューマーに、成功した場合と失敗した場合の両方を処理するように強制するように設計されたAPIです。例えば:

data Foo a =
     Success a
   | DidNotWorkBecauseOfA
   | DidNotWorkBecauseOfB

これは、成功のほかに関数に2つの追加の結果があることをプログラマに伝えます。


タイプセーフに関するチェック例外の場合は+1。私は以前にその考えを聞いたことがありませんが、それはとても理にかなっています。
Ixrec

11

IMO、チェックされた例外は、かなりまともなアイデアであったかもしれないものの欠陥のある実装です(完全に異なる状況の下で-しかし、実際には現在の状況では良いアイデアでさえありません)。

良い考えは、プログラムがスローする可能性のあるすべての例外がキャッチされることをコンパイラーに確認してもらうことです。実装の欠陥は、それを行うために、Javaが、チェックされた例外をスローするように宣言されているものを呼び出すクラスで、例外を直接処理することを強制することです。例外処理の最も基本的な考え方(および利点)の1つは、エラーが発生した場合、特定のエラーとは関係のないコードの中間層を許可し、その存在そのものを完全に無視できることです。チェックされた例外(Javaで実装されている)はそれを許可しないため、最初から例外処理の主要な(主な?)利点を破壊します。

理論的には、プログラム全体でのみ強制的にチェック例外を有効にすることができます。つまり、プログラムを「構築」しながら、プログラム内のすべての制御パスのツリーを構築します。ツリー内のさまざまなノードには、1)生成可能な例外、および2)キャッチ可能な例外で注釈が付けられます。プログラムが正しいことを確認するために、ツリーを歩いて、ツリー内の任意のレベルでスローされたすべての例外がツリー内のより高いレベルでキャッチされたことを確認します。

彼らそれをしなかった理由(とにかく推測する)、そうするのは耐え難いほど難しいだろうということです-実際、私は誰もがそれを非常に単純なもの(「おもちゃ")言語/システム。Javaの場合、動的なクラスロードなどにより、他の多くの場合よりもさらに難しくなります。さらに悪いことに、(Cのようなものとは異なり)Javaは(少なくとも通常は)静的にコンパイル/実行可能ファイルにリンクされていません。つまり、エンドユーザーが実際にプログラムのロードを開始して実行するまで、この種の強制を実行しようとするグローバルな認識をシステム内にまったく持たないということです。

その時点で施行を行うことは、いくつかの理由で非実用的です。まず第一に、せいぜい起動時間を延ばすことであり、これはすでにJavaに関して最も大きな、最も一般的な不満の1つです。第二に、良いことをするためには、プログラマーがそれを修正するのに十分早い段階で問題を検出する必要があります。ユーザーがプログラムをロードしているとき、あまり良いことをするには遅すぎます-その時点での唯一の選択肢は、例外があるかもしれないという機会に、問題を無視するか、プログラムのロード/実行をまったく拒否することですそれは捕まえられませんでした。

そのままでは、チェック例外は役に立たないよりも悪いですが、チェック例外の代替設計はさらに悪いです。チェック例外の基本的な考え方は良いもののように思えますが、実際にはあまり良くなく、たまたまJavaに特に適していません。通常、リフレクション/動的クラスロードのような完全な実行可能ファイルにコンパイル/リンクされるC ++のようなものについては、もう少しチャンスがあります(ただし、率直に言って、同じ種類の混乱なしに正しく強制することもできます)現在のJavaの原因は、おそらく現在の最新技術を超えているでしょう。


例外を直接処理する必要があるという最初の最初のステートメントはすでに偽です。throwsステートメントを追加するだけで、そのthrowsステートメントは親タイプのものになる場合があります。さらに、本当に処理する必要がないと思われる場合は、代わりに単にRuntimeExceptionに変換することができます。IDEは通常、両方のタスクを支援します。
マールテンボードー14

2
@owlstead:ありがとう-おそらく、その文言にはもっと注意を払うべきだったはずです。中間レベルのコードはすべて、例外を通過する可能性のあるすべての例外を認識する必要があるため、ポイントはそれほど例外ではなく、実際に例外をキャッチする必要がありました。残りに関しては、コードを大量かつ簡単に作成できるツールを使用しても、大量のコードを作成しているという事実は変わりません。
ジェリーコフィン14

チェックされた例外は、偶発的な事態 - 失敗とは異なる、成功以外の予測可能で回復可能な結果-予測不可能な低レベルまたはシステムの問題を対象としています。FileNotFoundまたはJDBC接続を開く際のエラーは、両方とも偶然と正しく見なされていました。残念ながら、Javaライブラリデザイナーは完全なミスを犯し、他のすべてのIOおよびSQL例外も「チェック済み」クラスに割り当てました。これらはほぼ完全に回復不能ですが。 literatejava.com/exceptions/...
トーマス・W

@owlstead:チェック済み例外は、中間呼び出しコードが予期する場合にスローされる場合にのみ、呼び出しスタックの中間層を介してバブリングする必要があります。私見、チェックされた例外は、呼び出し側がそれらをキャッチするか、それらが予想されたかどうかを示さなければならなかった場合、良いことだったでしょう。予期しない例外は、UnexpectedExceptionから派生した型で自動的にラップされる必要がありますRuntimeException。「スロー」と「予期しない」は矛盾しないことに注意してください。fooスローBozExceptionして呼び出した場合bar、それはbarスローすることを期待しているという意味ではありませんBozException
supercat

10

それらは、プログラマーの迷惑をかけ、例外の飲み込みを促すのに非常に適しています。例外の半分のポイントは、エラーを処理するための適切なデフォルトの方法があり、処理できないエラー条件を明示的に伝播する必要がないことです。(健全なデフォルトは、コードのどこでもエラーを処理しない場合、事実上、発生しないと断言し、間違っている場合は健全な終了とスタックトレースが残されていることです。)この。Cでエラーを明示的に伝播する必要があるように感じます。


4
簡単な修正:throws FileNotFoundException。ハードフィックス:catch (FileNotFoundException e) { throw RuntimeException(e); }
Thomas Eding

5

チェックされた例外は少し失敗した実験だと言ってもいいと思います。彼らが間違っていたのは、彼らがレイヤーを壊したということです:

  • モジュールAはモジュールBの上に構築される場合があります。
  • モジュールBは、モジュールCの上に構築できます。
  • モジュールCはエラーを識別する方法を知っているかもしれません。
  • モジュールAは、エラーの処理方法を知っている可能性があります。

AとCに限定されていた可能性のあるエラーの知識をBに分散させる必要があるため、懸念の良い分離は壊れています。

ある素敵な議論ウィキペディア上のチェック例外のは。

Bruce Eckelにも素敵な記事があります。引用です:

[T]プログラマに積み上げるランダムなルール、手近な問題の解決とは関係のないルールは、プログラマが作成できる速度が遅くなります。そして、これは線形因子ではないように見えますが、指数関数的なものです

C ++の場合、すべてのメソッドにthrows仕様句がある場合、素晴らしい最適化を行うことができます。とにかく、Java RuntimeExceptionsはチェックされていないので、これらの利点があるとは思わない。とにかく、最新のJITはコードを最適に最適化できるはずです。

AspectJの優れた機能の1つは、例外の緩和です。特に、階層化を強化するために、チェック済み例外を未チェック例外に変換できます。

おそらくそれは、Javaは、それがためにチェックしている可能性があり、このトラブルのすべてを行ったことは皮肉だnull代わりに、はるかに貴重だったかもしれません


笑、彼らがやった決定としてnullのことをチェックすることを考えたことはありません...しかし、私は最終的にObjective-Cで作業した後、nullが呼び出されるメソッドを持つことを可能にする自然な問題ではないことを理解しました。
ダンローゼンスターク

3
nullチェックは非常に大きな苦痛です-常にチェックする必要があります-プラスの名前が付けられた例外がそうする理由を伝えません。

5

例外が大好きです。

しかし、私は彼らの誤用に絶えず困惑しています。

  1. フロー処理には使用しないでください。例外は作成する必要があるオブジェクトであり、メモリなどを使用する必要があるため、何かを書くためにわざわざできなかったという理由だけで、何かをしようとするコードを望みません。通話を行う前にif()をチェックします。それは怠け者であり、栄光の例外に悪い名前を与えます。

  2. それらを飲み込まないでください。今まで。どんな状況下でも。最低限、ログファイルに何かを貼り付けて、例外が発生したことを示します。例外を飲み込むコードを自分の道で追跡しようとするのは悪夢です。繰り返しになりますが、強大な例外を評判の悪いものにしたことで、例外の飲み込み人をのろいなさい!

  3. OOの帽子をかぶって例外を処理してください。意味のある階層を持つ独自の例外オブジェクトを作成します。ビジネスロジックと例外を連動させて階層化します。以前は、システムの新しい領域で作業を開始した場合、コーディングの30分以内にExceptionをサブクラス化する必要があるため、最近はExceptionクラスを作成することから始めます。

  4. 「フレームワーク」コードのビットを書いている場合は、必要に応じて独自の新しい例外をスローするようにすべての初期インターフェースを作成してください... IOExceptionをスローしますが、書き込み先のインターフェイスが 'ReportingApplicationException'をスローしようとしています。

例外を機能させる方法を習得したら、振り返ることはありません。


2
良い指示のために+1。また、必要に応じinitCauseて、例外を引き起こした例外とともに使用または初期化します。例:書き込みが失敗した場合にのみ例外が必要なI / Oユーティリティがあります。ただし、書き込みが失敗する可能性がある複数の理由があります(アクセス、書き込みエラーなどを取得できませんでした)元の問題が飲み込まれないように、原因の例外を使用してカスタム例外をスローします。
マイケルK

3
私は失礼になりたくありませんが、これはCHECKED例外とどう関係しますか?:)
パルト

独自のチェック例外の階層を記述するように書き直すことができます。
マールテンボードー14

4

チェックされた例外もADAにあります。

(警告、この投稿には、あなたが直面するかもしれないと強く信じられている信念が含まれています。)

プログラマーはそれらが好きではなく、文句を言ったり、例外を飲み込むコードを書いたりしません。

チェックされた例外が存在するのは、物事が機能しないだけでなく、障害モード/効果分析を実行して事前に決定できるためです。

ファイルの読み取りが失敗する可能性があります。RPC呼び出しは失敗する可能性があります。ネットワークIOが失敗する可能性があります。解析時にデータが誤ってフォーマットされる可能性があります。

コードの「ハッピーパス」は簡単です。

私は、素晴らしい「ハッピーパス」コードを書くことができる大学の男を知っていました。エッジケースのどれも機能しませんでした。最近、彼はオープンソース企業のPythonをやっています。言っ途切れる。

チェックされた例外を処理したくない場合、あなたが本当に言っているのは

While I'm writing this code, I don't want to consider obvious failure modes.

The User will just have to like the program crashing or doing weird things.

But that's okay with me because 

I'm so much more important than the people who will have to use the software

in the real, messy, error-prone world.

After all, I write the code once, you use it all day long.

したがって、チェックされた例外は、より多くの作業を意味するため、プログラマーに好かれることはありません。

もちろん、他の人はその仕事をやりたいと思っているかもしれません。

ファイルサーバーに障害が発生した場合やUSBスティックが故障した場合でも、彼らは正しい答えを望んでいたかもしれません。

あなたの仕事がソフトウェアを書くことであるとき、あなたはあなたの人生をより楽にし、あなたが楽しむプログラミング言語を使うべきであるということは、プログラミングコミュニティにおける奇妙な信念です。あなたの仕事は誰かの問題を解決することであり、プログラムによるジャズの即興演奏に従事させることではありません。

あなたがアマチュアプログラマ(お金のためのプログラミングではない)である場合、C#またはチェックされた例外なしで他の言語で自由にプログラミングしてください。ちなみに、ロゴの中間者とプログラムを切り取ってください。カメを使って床にきれいな模様を描くことができます。


6
あなたがそこに着いた素敵な暴言。
アダムリア

2
+1 "これまでにうまく機能したエッジケースはありません。最近では、オープンソース企業のためにPythonをやっています。Nuffは言いました。" クラシック
ニムチンプスキー

1
@palto:Java は、簡単ではないパスを考慮するように強制します。それは大きな違いです。C#の場合、「例外は文書化されています」と言って、手を洗います。しかし、プログラマーはエラーを起こしやすいです。彼らは物事を忘れます。コードを書くとき、コンパイラが私に知らせてくれるあらゆる亀裂を100%思い出させたいです。
トーマスエディング

2
@ThomasEding Javaにより、メソッドが呼び出される例外を処理するように強制されます。呼び出し元のコードで実際に何かをしたいのは非常にまれであり、通常は例外をアプリケーションのエラー処理レイヤーにバブルアップします。その後、例外をランタイム例外にラップするか、独自の例外を作成する必要があります。怠zyなプログラマには3番目のオプションがあります。私は気にしないので無視してください。そのように、エラーが発生したことを誰も知らず、ソフトウェアには本当に奇妙なバグがあります。その3つ目は、未チェックの例外では発生しません。
パルト

3
@ThomasEdingこれを行うと、上記のすべてのインターフェースが変更され、アプリケーションのすべてのレイヤーに実装の詳細が不必要に明らかになります。例外が回復したいものであり、インターフェースで意味がある場合にのみ、それを行うことができます。一部の構成ファイルが欠落している可能性があるため、IOPExceptionをgetPeopleメソッドに追加したくありません。意味がありません。
-palto

4

チェックされた例外は、人々がそこに近いエラーを処理することを奨励しているはずです。ソースから離れるほど、実行中に終了する機能が増え、システムが一貫性のない状態に陥る可能性が高くなります。さらに、誤って誤った例外をキャッチする可能性が高くなります。

悲しいことに、人々は例外を無視するか、スロー仕様を増やし続ける傾向がありました。これらは両方とも、チェックされた例外が推奨するものであるという点を見逃しています。それらは、手続き型コードを変換して、おそらくOOにする多くのGetterおよびSetter関数を使用するようなものです。

基本的に、チェック例外は、コードのある程度の正確性を証明しようとしています。コードが実行される前に特定のものが正しいことを証明しようとするという点で、静的型付けとほとんど同じ考えです。問題は、その余分な証拠のすべてが努力を要することであり、その努力は価値があるということですか?


2
多くの場合、例外への最良の対応は、アプリをクラッシュさせることです。この観点の理論的基礎については、Bertrand MeyerのObject Oriented Software Constructionをお読みください。彼は、例外が適切に使用されている場合(契約違反)、本質的に2つの正しい応答があることを示しています:(1)アプリをクラッシュさせるか、(2)例外を引き起こした問題を修正してプロセスを再起動します。詳細については、Meyerをお読みください。コメントにまとめるのは難しいです。
クレイグスタンツ

1
@Craig Stuntz、例外を無視することで、それらを飲み込むことを意味しました。
ウィンストンイーバート

ああ、それは違う。飲み込んではいけないことに同意します。
クレイグスタンツ

「例外を無視するのはしばしば正しいことです。多くの場合、例外に対する最良の対応はアプリをクラッシュさせることです。」:それは実際にアプリケーションに依存します。Webアプリケーションでキャッチされない例外がある場合、起こり得る最悪の事態は、顧客がWebページにエラーメッセージを表示することを報告し、数分以内にバグ修正を展開できることです。飛行機の着陸中に例外が発生した場合は、それを処理して回復しようとする方が良いでしょう。
ジョルジオ14

@Giorgio、非常に真実。飛行機などの場合、回復する最良の方法はシステムを再起動することですか?
ウィンストンユワート14
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.