防御的プログラミング:Javaのガイドライン
-
22-07-2019 - |
質問
私は.NETのバックグラウンドで、今はJavaに手を出しています。
現在、誤った入力に対する防御的なAPIの設計には大きな問題があります。次のコードを取得したとしましょう(十分近い):
public void setTokens(Node node, int newTokens) {
tokens.put(node, newTokens);
}
ただし、このコードは次の2つの理由で失敗する可能性があります。
- ユーザーは
null
ノードを渡します。 - ユーザーが無効なノード、つまりグラフに含まれていないノードを渡します。
.NETでは、 ArgumentNullException
( NullReferenceException
!ではなく)または ArgumentException
をそれぞれ実行し、問題のある引数( node
)の名前を string
引数。
Javaには同等の例外はないようです。より具体的になり、状況を説明するのに最も近い例外を投げたり、特定の状況に合わせて独自の例外クラスを作成したりすることもできます。
これはベストプラクティスですか?または、.NETに ArgumentException
に似た汎用クラスがありますか?
この場合、 null
をチェックしても意味がありますか?とにかくコードは失敗し、例外のスタックトレースには上記のメソッド呼び出しが含まれます。 null
に対するチェックは冗長で過度に思えます。確かに、スタックトレースはわずかきれいになります(JREの HashMap
実装の内部チェックではなく、ターゲットが上記のメソッドであるため)。しかし、これは追加の if
ステートメントのコストに対して相殺する必要があります。さらに、とにかく never 発生する必要があります。結局、 null
上記の方法は予想される状況ではなく、かなり愚かなバグです。それはまったく妄想的であると予想されます-そして、私はそれをチェックしなくても同じ例外で失敗します。
[コメントで指摘されているように、 HashMap.put
は実際にはキーの null
値を許可します。そのため、ここでは null
に対するチェックは必ずしも冗長ではありません。]
解決
異なるグループには異なる基準があります。
まず、 RuntimeException
s(チェックなし)と通常の Exception
s(チェック済み)の違いを知っていると仮定します。そうでない場合は、この質問と回答。独自の例外を作成すると、強制的にキャッチできますが、 NullPointerException
と IllegalArgumentException
は一部のサークルで眉をひそめられるRuntimeExceptionです。
第二に、あなたと同じように、私が取り組んだグループは積極的にアサートを使用しませんが、チーム(またはAPIのコンシューマー)がアサートを使用することを決定した場合、正確に正しいメカニズムのような音をアサートします。
私があなただったら、 NullPointerException
を使用します。この理由は先例です。 SunのサンプルJava API、たとえば java.util.TreeSet 。これはまさにこのような状況のためにNPEを使用し、コードがヌルを使用したように見えますが、完全に適切です。
他の人が言っているように、 IllegalArgumentException
はオプションですが、NullPointerExceptionの方がより通信しやすいと思います。
このAPIが外部の企業/チームによって使用されるように設計されている場合、 NullPointerException
に固執しますが、javadocで宣言されていることを確認してください。内部で使用する場合は、独自の例外階層を追加する価値があると判断するかもしれませんが、個人的には printStackTrace()
dまたは記録される巨大な例外階層を追加するAPIを見つける努力の無駄です。
1日の終わりに、主なことは、コードが明確に通信することです。ローカルの例外階層はローカルの専門用語のようなものです。インサイダーに情報を追加しますが、アウトサイダーを困惑させる可能性があります。
nullに対するチェックに関しては、意味があると主張します。まず、役立つ例外を作成するときに、null(ノードまたはトークン)に関するメッセージを追加できます。第二に、将来的には null
を許可する Map
実装を使用する可能性があり、その後エラーチェックが失われます。コストはほとんどないので、プロファイラーが内部ループの問題であると言わない限り、心配することはありません。
他のヒント
標準のJava例外は IllegalArgumentException
です。引数がnullの場合、 NullPointerException
をスローする人もいますが、私にとってはNPEに「誰かが台無しにされた」というものがあります。意味があり、APIのクライアントに自分が何をしているのかわからないと思わせたくない。
パブリックAPIの場合、引数を確認し、早期かつクリーンに失敗します。時間/コストはほとんど問題になりません。
Javaでは、通常IllegalArgumentExceptionがスローされます
優れたJavaコードの記述方法についてのガイドが必要な場合は、 Joshua Blochによる効果的なJava 。
これは、アサート:
public void setTokens(Node node, int newTokens) {
assert node != null;
tokens.put(node, newTokens);
}
あなたのアプローチは、関数が呼び出し元に提供するコントラクトに完全に依存します-ノードがnullではないという前提条件ですか?
その場合、ノードがnullの場合、コントラクト違反であるため例外をスローする必要があります。そうでない場合、関数はnullノードをサイレントに処理し、適切に応答する必要があります。
多くは、メソッドのコントラクトと呼び出し元がどれだけよく知られているかに依存すると思います。
プロセスのある時点で、呼び出し元はアクションを実行して、メソッドを呼び出す前にノードを検証できます。呼び出し元を知っていて、これらのノードが常に検証されていることを知っているなら、良いデータが得られると仮定しても大丈夫だと思います。本質的に責任は呼び出し側にあります。
ただし、たとえば、配布されているサードパーティライブラリを提供している場合は、nullなどについてノードを検証する必要があります...
illegalArugementExceptionはJava標準ですが、RunTimeExceptionでもあります。そのため、呼び出し元に例外を処理させるには、チェック例外、おそらく作成したカスタム例外を提供する必要があります。
個人的にはNullPointerExceptionsが偶然にのみ発生するようにしたいので、不正な引数値が渡されたことを示すために他の何かを使用する必要があります。 IllegalArgumentExceptionはこれに適しています。
if (arg1 == null) {
throw new IllegalArgumentException("arg1 == null");
}
これは、コードを読んでいる人だけでなく、午前3時にサポートコールを受ける貧しい人々にとっても十分なはずです。
(そして、常に例外の説明テキストを提供します。悲しい日には感謝します)
他と同様:java.lang.IllegalArgumentException。 nullノードのチェックについて、ノード作成時の不正な入力のチェックについてはどうですか?
誰も喜ばせる必要はないので、標準コードとして今やっていることは
void method(String s)
if((s != null) && (s instanceof String) && (s.length() > 0x0000))
{
これは私に多くの睡眠を与えます。
その他は同意しません。