アサーションの重要な役割を理解するための実際の例は何ですか?
19 に答える
アサーション(assertキーワードによる)がJava1.4で追加されました。これらは、コード内の不変条件の正当性を検証するために使用されます。これらは本番コードでトリガーされるべきではなく、コードパスのバグまたは誤用を示しています。-ea
これらは、コマンドのオプションを使用して実行時にアクティブ化できますがjava
、デフォルトではオンになりません。
例:
public Foo acquireFoo(int id) {
Foo result = null;
if (id > 50) {
result = fooService.read(id);
} else {
result = new Foo(id);
}
assert result != null;
return result;
}
アサーションは、コード内のバグをキャッチするための開発段階のツールです。これらは簡単に削除できるように設計されているため、製品コードには存在しません。したがって、アサーションは、顧客に提供する「ソリューション」の一部ではありません。これらは、作成している仮定が正しいことを確認するための内部チェックです。最も一般的な例は、null のテストです。多くのメソッドは次のように記述されています。
void doSomething(Widget widget) {
if (widget != null) {
widget.someMethod(); // ...
... // do more stuff with this widget
}
}
このようなメソッドでは、ウィジェットが null であってはならないことがよくあります。したがって、null の場合は、追跡する必要があるコードのどこかにバグがあります。しかし、上記のコードはこれを教えてくれません。したがって、「安全な」コードを書こうとする善意の努力の中で、バグを隠していることにもなります。次のようなコードを書く方がはるかに優れています。
/**
* @param Widget widget Should never be null
*/
void doSomething(Widget widget) {
assert widget != null;
widget.someMethod(); // ...
... // do more stuff with this widget
}
このようにして、このバグを早期に発見することができます。(このパラメーターが null であってはならないことをコントラクトで指定することも役立ちます。) 開発中にコードをテストするときは、必ずアサーションをオンにしてください。(そして、同僚にこれを行うよう説得することもしばしば困難であり、私は非常に迷惑だと感じています。)
さて、同僚の何人かはこのコードに反対し、運用環境での例外を防ぐためにヌル チェックを追加する必要があると主張します。その場合でも、アサーションは役に立ちます。次のように記述できます。
void doSomething(Widget widget) {
assert widget != null;
if (widget != null) {
widget.someMethod(); // ...
... // do more stuff with this widget
}
}
こうすれば、製品コードに null チェックがあることを同僚は喜ぶでしょうが、開発中は、ウィジェットが null の場合にバグを隠す必要がなくなります。
実際の例を次に示します。私はかつて、2 つの任意の値が等しいかどうかを比較するメソッドを作成しました。この場合、いずれかの値が null である可能性があります。
/**
* Compare two values using equals(), after checking for null.
* @param thisValue (may be null)
* @param otherValue (may be null)
* @return True if they are both null or if equals() returns true
*/
public static boolean compare(final Object thisValue, final Object otherValue) {
boolean result;
if (thisValue == null) {
result = otherValue == null;
} else {
result = thisValue.equals(otherValue);
}
return result;
}
このコードはequals()
、thisValue が null でない場合にメソッドの作業を委任します。ただし、null パラメーターを適切に処理することにより、equals()
メソッドが の契約を正しく満たしていることを前提としています。equals()
同僚が私のコードに反対し、私たちのクラスの多くにはequals()
null をテストしないバグのあるメソッドがあるので、そのチェックをこのメソッドに入れる必要があると言いました。これが賢明なのか、それともエラーを強制的に検出して修正できるようにするべきなのかについては議論の余地がありますが、私は同僚に任せて、コメントでマークした null チェックを入れました。
public static boolean compare(final Object thisValue, final Object otherValue) {
boolean result;
if (thisValue == null) {
result = otherValue == null;
} else {
result = otherValue != null && thisValue.equals(otherValue); // questionable null check
}
return result;
}
ここでの追加のチェックは、メソッドがコントラクトで要求されている null のチェックに失敗したother != null
場合にのみ必要です。equals()
バグのあるコードをコード ベースに残すことの賢明さについて、同僚と実りのない議論をする代わりに、コードに 2 つのアサーションを入れただけです。これらのアサーションは、開発段階で、クラスの 1 つが適切に実装されていない場合に通知してくれるので、equals()
修正できます。
public static boolean compare(final Object thisValue, final Object otherValue) {
boolean result;
if (thisValue == null) {
result = otherValue == null;
assert otherValue == null || otherValue.equals(null) == false;
} else {
result = otherValue != null && thisValue.equals(otherValue);
assert thisValue.equals(null) == false;
}
return result;
}
留意すべき重要な点は次のとおりです。
アサーションは開発段階のツールのみです。
アサーションのポイントは、コードだけでなくコード ベースにもバグがあるかどうかを知らせることです。(ここでのアサーションは、実際には他のクラスのバグにフラグを立てます。)
クラスが適切に作成されていると同僚が確信していたとしても、ここでのアサーションは依然として有用です。null のテストに失敗する可能性のある新しいクラスが追加され、このメソッドはそれらのバグにフラグを立てることができます。
開発中は、記述したコードでアサーションが使用されていなくても、常にアサーションをオンにする必要があります。私のIDEは、新しい実行可能ファイルに対してデフォルトで常にこれを行うように設定されています。
アサーションは本番環境でのコードの動作を変更しないので、私の同僚は、null チェックがあること、および
equals()
メソッドにバグがあってもこのメソッドが適切に実行されることを喜んでいます。equals()
開発中のバグのあるメソッドをキャッチしてくれるので嬉しいです。
また、失敗する一時的なアサーションを入れてアサーション ポリシーをテストする必要があります。これにより、ログ ファイルまたは出力ストリームのスタック トレースを通じて確実に通知を受けることができます。
アサートはいつ使用する必要がありますか?
キーワードが何をするかを説明する良い回答はたくさんありますが、「キーワードは実生活でいつ使用する必要があるassert
か」という本当の質問に答えるものはほとんどありません。答え:assert
ほとんどは決してない
コンセプトとしてのアサーションは素晴らしいものです。優れたコードには、多くのif (...) throw ...
ステートメント (およびその親戚であるObjects.requireNonNull
and などMath.addExact
) があります。assert
ただし、特定の設計上の決定により、キーワード自体の有用性が大幅に制限されています。
キーワードの背後にある原動力assert
は時期尚早の最適化であり、主な機能はすべてのチェックを簡単にオフにできることです。実際、チェックassert
はデフォルトでオフになっています。
ただし、不変チェックが本番環境で引き続き実行されることが非常に重要です。これは、完全なテスト カバレッジは不可能であり、すべての製品コードには診断と軽減に役立つバグが含まれているためです。
したがって、if (...) throw ...
パブリック メソッドのパラメーター値のチェックや のスローに必要なのと同様に、 の使用を優先する必要がありますIllegalArgumentException
。
場合によっては、処理に望ましくないほど長い時間がかかる不変チェックを書きたくなるかもしれません (そして、それが問題になるほど頻繁に呼び出されます)。ただし、このようなチェックはテストの速度を低下させますが、これも望ましくありません。このような時間のかかるチェックは通常、単体テストとして記述されます。assert
それでも、この理由で使用するのが理にかなっている場合があります。
assert
よりもきれいできれいだからという理由だけで使用しないでくださいif (...) throw ...
(私はきれいでかわいいのが好きなので、非常に苦痛でそう言います)。自分でどうすることもできず、アプリケーションの起動方法を制御できる場合は、自由に使用できますassert
が、本番環境では常にアサーションを有効にしてください。確かに、これは私がやりがちなことです。assert
のように振る舞うロンボク注釈を求めていif (...) throw ...
ます。ここに投票してください。
(暴言: JVM 開発者はひどい、時期尚早に最適化するコーダーの集まりでした。それが、Java プラグインと JVM における非常に多くのセキュリティ問題について耳にする理由です。彼らは製品コードに基本的なチェックとアサーションを含めることを拒否しました。料金をお支払いください。)
最も一般的な使用例を次に示します。列挙値をオンにするとします。
switch (fruit) {
case apple:
// do something
break;
case pear:
// do something
break;
case banana:
// do something
break;
}
すべてのケースを処理する限り、問題ありません。しかしいつか、誰かがあなたの enum に fig を追加し、それを switch ステートメントに追加するのを忘れるでしょう。これにより、switch ステートメントを終了するまで効果が感じられないため、見つけにくいバグが発生します。しかし、スイッチを次のように書くと、すぐにキャッチできます。
switch (fruit) {
case apple:
// do something
break;
case pear:
// do something
break;
case banana:
// do something
break;
default:
assert false : "Missing enum value: " + fruit;
}
アサーションは、事後条件と「失敗しない」事前条件をチェックするために使用されます。正しいコードがアサーションに失敗することはありません。それらがトリガーされた場合、バグを示す必要があります (問題の実際の場所に近い場所であることが望ましい)。
アサーションの例としては、メソッドの特定のグループが正しい順序で呼び出されていることを確認することがあります (たとえば、 でhasNext()
前next()
に呼び出されているIterator
)。
スタッククラスからの実際の例 ( Java Articles のアサーションから)
public int pop() {
// precondition
assert !isEmpty() : "Stack is empty";
return stack[--num];
}
ここで提供されるすべての優れた回答に加えて、公式の Java SE 7 プログラミング ガイドには、使用に関する非常に簡潔なマニュアルがassert
あります。アサーションを使用するのが良い (そして重要なことに、悪い) アイデアである場合と、それが例外をスローすることとどのように異なるかについて、いくつかの具体的な例を示します。
Assert は、開発時に非常に役立ちます。コードが正しく機能している場合に何かが起こらない場合に使用します。使いやすく、実際にはオフになるため、コードに永遠にとどまることができます。
この状態が実際に発生する可能性がある場合は、それに対処する必要があります。
気に入っていますが、Eclipse/Android/ADT で有効にする方法がわかりません。デバッグ時もオフのようです。(これにはスレッドがありますが、ADT 実行構成には表示されない「Java vm」を参照しています)。
アサーションは、オフになる可能性のあるチェックです。それらはめったに使用されません。なんで?
- これらは制御できないため、パブリック メソッドの引数のチェックには使用しないでください。
result != null
このようなチェックは非常に高速で、保存するものがほとんどないため、単純なチェックには使用しないでください。
それで、何が残っていますか?実際に真であると予想される条件の高価なチェック。良い例は、RB ツリーのようなデータ構造の不変条件です。実際、JDK8 には、.ConcurrentHashMap
TreeNodes
- ランタイムを簡単に支配する可能性があるため、本番環境でそれらをオンにすることは本当に望ましくありません。
- テスト中にオンまたはオフに切り替えることができます。
- コードで作業するときは、必ずそれらをオンにする必要があります。
小切手はそれほど高くない場合もありますが、同時に、合格することは間違いありません。私のコードには、例えば、
assert Sets.newHashSet(userIds).size() == userIds.size();
ここで、作成したばかりのリストには固有の要素があると確信していますが、それを文書化して再確認したかったのです。
アサーションは基本的にアプリケーションをデバッグするために使用されるか、アプリケーションの有効性をチェックするために一部のアプリケーションの例外処理の代わりに使用されます。
アサーションは実行時に機能します。概念全体を非常に簡単に説明できる簡単な例をここに示します-assertキーワードはJavaで何をしますか?(WikiAnswers)。
アサーションはデフォルトで無効になっています。それらを有効にするには、-ea
オプションを指定してプログラムを実行する必要があります (粒度は変更できます)。たとえば、java -ea AssertionsDemo
.
アサーションを使用するには、次の 2 つの形式があります。
- シンプル:例えば。
assert 1==2; // This will raise an AssertionError
. - より良い:
assert 1==2: "no way.. 1 is not equal to 2";
これにより、指定されたメッセージも表示されて AssertionError が発生するため、より優れています。実際の構文でassert expr1:expr2
は expr2 が値を返す任意の式になる場合がありますが、メッセージを出力するためだけに使用することが多くなりました。
assert
がキーワードです。JDK 1.4 で導入されました。は 2 種類のassert
sです。
- 非常に簡単な
assert
ステートメント - 簡単な
assert
ステートメント。
デフォルトでは、すべてのassert
ステートメントは実行されません。ステートメントが false を受け取ると、assert
自動的にアサーション エラーが発生します。