質問
私は素晴らしい本を読んでいる途中です クリーンなコード
議論の 1 つは、メソッドに null を渡すことに関するものです。
public class MetricsCalculator {
public double xProjection(Point p1, Point p2) {
return (p2.x - p1.x) * 1.5;
}
}
...
calculator.xProjection(null, new Point(12,13));
これは、これを処理するさまざまな方法を表しています。
public double xProjection(Point p1, Point p2) {
if (p1 == null || p2 == null) {
throw new IllegalArgumentException("Invalid argument for xProjection");
}
return (p2.x - p1.x) * 1.5;
}
public double xProjection(Point p1, Point p2) {
assert p1 != null : "p1 should not be null";
assert p2 != null : "p2 should not be null";
return (p2.x - p1.x) * 1.5;
}
私は アサーション しかし、私はアサーションがデフォルトでオフになっているという事実が好きではありません。
この本では最後に次のように述べられています。
ほとんどのプログラミング言語では、呼び出し元によって誤って渡された null を処理する良い方法がありません。このような場合があるため、合理的なアプローチは、デフォルトで null を渡すことを禁止することです。
この制限をどのように強制するかについては詳しく説明されていません。
どちらにしても強い意見をお持ちの方はいらっしゃいますか。
解決
ここでは、アサーションの使用と例外のスローの両方が有効なアプローチです。ここの場合のように、どちらのメカニズムも実行時エラーではなくプログラミング エラーを示すために使用できます。
- アサーションは通常、運用システムでは無効になっているため、パフォーマンスの面で利点があります。
- 例外にはチェックが常に実行されるため、安全という利点があります。
実際の選択は、プロジェクトの開発慣行によって異なります。プロジェクト全体として、アサーション ポリシーを決定する必要があります。すべての開発中にアサーションを有効にするという選択がある場合は、この種の無効なパラメーターをチェックするためにアサーションを使用することをお勧めします。運用システムでは、プログラミング エラーによってスローされた NullPointerException をキャッチして処理できる可能性は低いです。いずれにしても意味のある方法で行われるため、アサーションと同じように機能します。
しかし実際には、アサーションが適切なときに有効になることを信頼せず、NullPointerException をスローする安全性を選択する開発者が多くいることを私は知っています。
もちろん、コードにポリシーを適用できない場合 (たとえば、ライブラリを作成していて、他の開発者によるコードの実行方法に依存している場合)、それらに対して NullPointerException をスローする安全なアプローチを選択する必要があります。ライブラリの API の一部であるメソッド。
他のヒント
一般的なルールは、メソッドが期待していない場合です。 null
引数をスローする必要があります System.ArgumentNullException. 。適当に投げる Exception
これは、リソースの破損やその他の悪影響からユーザーを保護するだけでなく、コードのユーザーがコードのデバッグに費やす時間を節約するためのガイドとしても機能します。
に関する記事もお読みください 防御的なプログラミング
これもすぐには役に立ちませんが、Spec# の言及に関連しています...Java の将来のバージョンに「null セーフ型」を追加するという提案があります。 「強化された null 処理 - Null セーフ型」.
この提案では、あなたの方法は次のようになります。
public class MetricsCalculator {
public double xProjection(#Point p1, #Point p2) {
return (p2.x - p1.x) * 1.5;
}
}
どこ #Point
非のタイプですnull
型のオブジェクトへの参照 Point
.
この制限をどのように強制するかについては詳しく説明されていません。
を投げることでそれを強制します 引数例外 null を渡す場合。
if (p1 == null || p2 == null) {
throw new IllegalArgumentException("Invalid argument for xProjection");
}
私はアサーションを使用することを好みます。
アサーションは public メソッドと protected メソッドでのみ使用するというルールがあります。これは、呼び出し側メソッドが有効な引数をプライベート メソッドに確実に渡す必要があると考えているためです。
スペック番号はとても面白そうです!
そのようなものが利用できない場合、私は通常、実行時の null チェックと内部メソッドのアサーションを使用して非プライベート メソッドをテストします。各メソッドで null チェックを明示的にコーディングするのではなく、check null メソッドを使用してそれをユーティリティ クラスに委譲します。
/**
* Checks to see if an object is null, and if so
* generates an IllegalArgumentException with a fitting message.
*
* @param o The object to check against null.
* @param name The name of the object, used to format the exception message
*
* @throws IllegalArgumentException if o is null.
*/
public static void checkNull(Object o, String name)
throws IllegalArgumentException {
if (null == o)
throw new IllegalArgumentException(name + " must not be null");
}
public static void checkNull(Object o) throws IllegalArgumentException {
checkNull(o, "object");
}
// untested:
public static void checkNull(Object... os) throws IllegalArgumentException {
for(Object o in os) checkNull(o);
}
次に、チェックは次のようになります。
public void someFun(String val1, String val2) throws IllegalArgumentException {
ExceptionUtilities.checkNull(val1, "val1");
ExceptionUtilities.checkNull(val2, "val2");
/** alternatively:
ExceptionUtilities.checkNull(val1, val2);
**/
/** ... **/
}
それ エディター マクロまたはコード処理スクリプトを使用して追加できます。編集: この方法でも冗長チェックを追加できますが、1 行の追加を自動化する方がはるかに簡単だと思います。
ほとんどのプログラミング言語では、呼び出し元によって誤って渡された null を処理する良い方法がありません。このような場合があるため、合理的なアプローチは、デフォルトで null を渡すことを禁止することです。
見つけました ジェットブレインズ' @Nullable
そして @NotNull
これに対処するためのアノテーション アプローチは、これまでのところ最も独創的です。残念ながら、これは IDE 固有ですが、非常にクリーンで強力です、私の意見では。
http://www.jetbrains.com/idea/documentation/howto.html
これ (または類似のもの) が Java 標準として採用されると、非常に素晴らしいでしょう。
厳密には関係ありませんが、以下を参照してください。 仕様番号.
まだ開発中 (Microsoft による) だと思いますが、いくつかの CTP が利用可能であり、期待できそうです。基本的に、これにより次のことが可能になります。
public static int Divide(int x, int y)
requires y != 0 otherwise ArgumentException;
{
}
または
public static int Subtract(int x, int y)
requires x > y;
ensures result > y;
{
return x - y;
}
また、Notnull 型などの別の機能も提供します。.NET Framework 2.0 上に構築されており、完全な互換性があります。ご覧のとおり、構文は C# です。
@Chris Karcher私は完全に正しいと言えます。私が言える唯一のことは、パラメータを個別にチェックし、null の発生元を追跡するのがはるかに簡単になるように、例外で null だったパラメータも報告させることです。
@wvdschel すごいですね!コードを書くのが大変な場合は、次のようなことを検討してください。 ポストシャープ (または Java 同等のものが利用可能な場合)、アセンブリを後処理し、パラメータ チェックを挿入できます。
オフトピックが話題になっているようなので、Scala はこれに対して興味深いアプローチをとります。すべての型は、明示的にラップしない限り、null ではないと想定されます。 Option
null である可能性があることを示します。それで:
// allocate null
var name : Option[String]
name = None
// allocate a value
name = Any["Hello"]
// print the value if we can
name match {
Any[x] => print x
_ => print "Nothing at all"
}
作業が遅くなるだけなので、通常はどちらも行わないことを好みます。いずれにせよ、NullPointerException は後でスローされるため、ユーザーはメソッドに null を渡していることにすぐに気づきます。以前はチェックを行っていましたが、最終的にコードの 40% がチェック コードになり、その時点で、素晴らしいアサーション メッセージを受け取る価値がないと判断しました。
同意または反対します wvdschelの投稿, 、それは彼が具体的に何を言っているかによります。
この場合、確かに、このメソッドはクラッシュします。 null
したがって、ここでの明示的なチェックはおそらく必要ありません。
ただし、メソッドが単に渡されたデータを保存するだけで、後でそれを処理する他のメソッドを呼び出す場合は、 不正な入力をできるだけ早く発見することが、バグをより早く修正するための鍵です. 。その後の時点では、無数の方法で不正なデータがたまたまクラスに与えられた可能性があります。それは、ネズミが事後にどのようにして家に入ってきたのかを解明しようとして、どこかに穴を見つけようとしているようなものです。
少し話が逸れましたが、特徴としては、 バグを見つける 私が非常に便利だと思うのは、メソッドのパラメータに注釈を付けて、どのパラメータに null 値を渡してはいけないかを記述できることです。
コードの静的分析を使用すると、 バグを見つける その後、潜在的に null 値を使用してメソッドが呼び出される場所を指摘できます。
これには次の 2 つの利点があります。
- アノテーションは、メソッドの呼び出し方法の意図を説明し、ドキュメント化を支援します。
- FindBugs は、メソッドの潜在的な問題の呼び出し元をポイントできるため、潜在的なバグを追跡できます。
メソッドを呼び出すコードにアクセスできる場合にのみ役立ちますが、通常はこれに当てはまります。
C#を投げる ArgumentException
, 、または Java IllegalArgumentException
この方法の冒頭にあるのが、私には最も明確な解決策であると思われます。
実行時例外、つまりメソッド シグネチャで宣言されていない例外には常に注意する必要があります。コンパイラはこれらを捕捉することを強制しないので、それらを忘れてしまうのは非常に簡単です。ソフトウェアが突然停止しないように、何らかの「キャッチオール」例外処理を備えていることを確認してください。それはユーザーエクスペリエンスの最も重要な部分です。
これに対処する最善の方法は、例外を使用することです。最終的に、アサートは最終的に次の結果を与えることになります。 似ている エンド ユーザーにはエクスペリエンスを提供しますが、コードを呼び出す開発者がエンド ユーザーに例外を表示する前に状況を処理する方法は提供されません。最終的には、無効な入力をできるだけ早く (特に公開コードで) テストし、呼び出し元のコードがキャッチできる適切な例外を提供する必要があります。
Java の方法では、null がプログラミング エラーから来ていると仮定します (つまり、テストフェーズの外に出てはなりません)、システムをそのままにしてスローするか、その時点に達する副作用がある場合は、最初に null をチェックして IllegalArgumentException または NullPointerException をスローします。
null が実際の値から得られる可能性がある場合 例外ただし、そのためにチェック例外を使用したくない場合は、メソッドの先頭で IllegalArgumentException ルートに進む必要があります。