Javaのassertキーワードは何をしますか、いつ使用する必要がありますか?


601

アサーションの重要な役割を理解するための実際の例は何ですか?


8
実際の生活では、ほとんど目にすることはありません。推測:アサーションを使用する場合、3つの状態について考える必要があります。アサートは成功、アサートは失敗、アサートは2つではなくオフになっています。そして、assertはデフォルトでオフになっているため、それが最も可能性の高い状態であり、コードで有効になっていることを確認することは困難です。つまり、アサートは使用が制限される時期尚早の最適化であるということです。@Bjornの回答を見るとわかるように、常にアサートを失敗させたくないユースケースを考え出すことはさらに困難です。
Yishai

35
@Yishai: 「アサートがオフになっていることを考える 必要がありますこれを行う必要がある場合は、間違っています。「アサートは使用が制限された時期尚早の最適化です」これはかなり軌道に乗っていません。これがSunの
見解

5
@DavidTonhofer、実際にはほとんど見かけません。これは検証可能です。オープンソースプロジェクトをいくつでもチェックしてください。不変条件を検証しないと言っているのではありません。それは同じことではありません。別の言い方をすると。アサートが非常に重要である場合、なぜデフォルトでオフになっているのですか?
Yishai 2014年

17
リファレンス、FWIW:ソフトウェアアサーションとコード品質の関係「アサーションの有効性を、ソースコードの静的分析ツールなどの一般的なバグ検索手法の有効性と比較します。ケーススタディから、アサーション密度の増加に伴い、ファイルでは、障害密度が統計的に有意に減少しています。」
David Tonhofer 2014年

4
@DavidTonhofer David、あなたの主張に対するあなたの愛はあなたたちがやっている非常に特定のタイプのプログラミングに対するものだと思います、私の分野で何らかの理由でプログラムを終了するWebアプリケーションで動作するのは最大のNO NOです-個人的にはユニット/
整数

回答:


426

アサーションassertキーワードによる)はJava 1.4で追加されました。これらは、コード内の不変条件の正しさを検証するために使用されます。これらは本番用コードでトリガーされることはなく、バグまたはコードパスの誤用を示しています。これらは-eajavaコマンドのオプションを使用して実行時にアクティブ化できますが、デフォルトではオンになっていません。

例:

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;
}

71
実際、Oracleはassertpublicメソッドのパラメーター(docs.oracle.com/javase/1.4.2/docs/guide/lang/assert.html)のチェックに使用しないように指示しています。それはExceptionプログラムを殺すのではなく、スローするはずです。
SJuan76 2013

10
しかし、なぜそれらが存在するのか説明しません。if()チェックを実行して例外をスローできないのはなぜですか?
El Mac

7
@ElMac-アサーションはサイクルのdev / debug / test部分用です-それらは本番用ではありません。ifブロックは本番環境で実行されます。単純なアサーションは銀行を壊しませんが、複雑なデータ検証を行う高価なアサーションは本番環境をダウンさせる可能性があるため、そこで無効にされます。
hoodaticus

2
@hoodaticusは、製品コードのすべてのアサーションをオン/オフにできるという事実だけが理由ですか?とにかく複雑なデータ検証を実行して、例外を除いて処理できるからです。プロダクションコードがある場合、複雑な(そしておそらく高価な)アサーションをオフにすることができます。理論的には、問題が発生するため、プログラムを停止させるべきではありません。
El Mac

8
This convention is unaffected by the addition of the assert construct. Do not use assertions to check the parameters of a public method. An assert is inappropriate because the method guarantees that it will always enforce the argument checks. It must check its arguments whether or not assertions are enabled. Further, the assert construct does not throw an exception of the specified type. It can throw only an AssertionError. docs.oracle.com/javase/8/docs/technotes/guides/language/…– Bakhshi '26
07/26

325

あなたが原子力発電所を制御するプログラムを書くことになっていると仮定しましょう。最も小さな間違いでも壊滅的な結果をもたらす可能性があることは明らかであり、そのため、コードにバグがない必要があります(引数のためにJVMにバグがないと仮定します)。

Javaは検証可能な言語ではありません。つまり、操作の結果が完全であることを計算することはできません。これの主な理由はポインターです:それらはどこでもどこでも指すことができるため、少なくともコードの妥当な範囲内では、この正確な値であると計算することはできません。この問題を考えると、コードが全体として正しいことを証明する方法はありません。しかし、あなたができることは、それが起こったときに少なくともすべてのバグを見つけたことを証明することです。

このアイデアは、契約による設計(DbC)パラダイムに基づいています。最初に(数学的な精度で)メソッドの実行内容を定義し、次に、実際の実行中にテストすることによってこれを検証します。例:

// Calculates the sum of a (int) + b (int) and returns the result (int).
int sum(int a, int b) {
  return a + b;
}

これは問題なく機能することは明らかですが、ほとんどのプログラマーはこのバグの中に隠れたバグを見ることはありません(ヒント:Ariane Vは同様のバグのためにクラッシュしました)。現在、DbCでは、関数の入力と出力を常にチェックして、正しく機能したことを確認する必要あると定義しています。Javaはこれをアサーションを通じて実行できます。

// Calculates the sum of a (int) + b (int) and returns the result (int).
int sum(int a, int b) {
    assert (Integer.MAX_VALUE - a >= b) : "Value of " + a + " + " + b + " is too large to add.";
  final int result = a + b;
    assert (result - a == b) : "Sum of " + a + " + " + b + " returned wrong sum " + result;
  return result;
}

この関数が失敗した場合は、それに気づくでしょう。あなたはあなたのコードに問題があることを知っているでしょう、あなたはそれがどこにあるのか、そしてあなたはそれを引き起こした原因を知っています(例外と同様に)。さらに重要なことは、次のコードが間違った値で動作するのを防ぎ、制御するものに損傷を与える可能性があるときに、実行を停止することです。

Java例外も同様の概念ですが、すべての検証に失敗します。(実行速度を犠牲にして)さらに多くのチェックが必要な場合は、アサーションを使用する必要があります。そうするとコードが肥大化しますが、最終的には驚くほど短い開発時間で製品を提供できます(バグを早期に修正するほど、コストは低くなります)。さらに、コード内にバグがある場合は、それを検出します。バグがすり抜けて後で問題が発生することはありません。

これはバグのないコードを保証するものではありませんが、通常のプログラムよりもはるかに近いコードです。


29
この例を選択したのは、バグのないように見えるコードに隠れたバグが非常によく示されているためです。これが他の誰かが提示したものと似ている場合、おそらく彼らは同じ考えを持っているでしょう。;)
TwoThe 2013年

8
アサーションがfalseの場合は失敗するため、アサートを選択します。ifは何らかの振る舞いを持つことができます。フリンジケースを打つことはユニットテストの仕事です。Design by Contractを使用すると、契約はかなり適切に指定されますが、実際の契約と同様に、契約が尊重されるようにするためのコントロールが必要です。アサーションを使用するとウォッチドッグが挿入され、契約が軽視されたときにユーザーに通知されます。外で何かをしたり、契約書に反対したりするたびに「間違った」と叫びながら、あなたが仕事を続けることができず、契約書に違反することができないように家に帰る、しつこい弁護士と考えてください。
Eric

5
この単純な場合に必要です:いいえ、ただしDbCはすべての結果をチェックする必要があると定義してます。誰かがその機能をもっと複雑なものに変更し、その後ポストチェックを適応させなければならない場合を想像してみてください、そしてそれは突然有用になります。
TwoThe

4
これを復活させて申し訳ありませんが、私は特定の質問があります。@TwoTheが何をnew IllegalArgumentExceptionしたのか、assertを使用する代わりに単にメッセージでa をスローすることの違いは何ですか?throwsつまり、メソッドの宣言とコードに追加して、その例外をどこかで管理することは別です。なぜassert新しい例外を投げるのですか?または、if代わりにではないのassertですか?これを実際に取得することはできません:(
Blueriver '13

14
-1:オーバーフローをチェックするアサーションaは、負になる可能性がある場合は間違っています。2番目のアサーションは無意味です。int値の場合、常にa + b-b == aになります。そのテストは、コンピューターが根本的に壊れている場合にのみ失敗する可能性があります。この不測の事態を防ぐには、複数のCPU間で一貫性を確認する必要があります。
ケビンクライン14

63

アサーションは、コードのバグをキャッチする開発段階のツールです。これらは簡単に削除できるように設計されているため、製品コードには存在しません。したがって、アサーションは、顧客に提供する「ソリューション」の一部ではありません。これらは、行っている仮定が正しいことを確認するための内部チェックです。最も一般的な例は、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にしないことをコントラクトで指定することも役立ちます。)開発中にコードをテストするときは、必ずアサーションをオンにしてください。(そして、これを行うようにあなたの同僚を説得することもしばしば困難であり、私はそれを非常に苛立たせます。)

さて、あなたの同僚の何人かはこのコードに異議を唱え、プロダクションでの例外を防ぐためにnullチェックを入れるべきだと主張します。その場合でも、アサーションは役に立ちます。次のように書くことができます:

void doSomething(Widget widget) {
  assert widget != null;
  if (widget != null) {
    widget.someMethod(); // ...
    ... // do more stuff with this widget
  }
}

このようにして、同僚はプロダクションコードにnullチェックがあることに満足しますが、開発中は、ウィジェットがnullのときにバグを非表示にすることはありません。

これが実際の例です。私はかつて、2つの任意の値が等しいかどうかを比較するメソッドを作成しました。

/**
 * 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;
}

ここでの追加のチェックother != nullは、equals()メソッドがその契約で要求されているnullのチェックに失敗した場合にのみ必要です。

バグのあるコードをコードベースに残すという知恵について、同僚と実りのない議論をするのではなく、コードに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;
}

覚えておくべき重要な点は次のとおりです。

  1. アサーションは開発段階のツールのみです。

  2. アサーションの要点は、コードだけでなくコードベースにもバグがあるかどうかを知らせることです。(ここでのアサーションは、実際には他のクラスのバグにフラグを立てます。)

  3. 私のクラスが適切に記述されていると私の同僚が確信していたとしても、ここでのアサーションはまだ役に立ちます。nullのテストに失敗する可能性のある新しいクラスが追加され、このメソッドはこれらのバグにフラグを付けることができます。

  4. 開発では、作成したコードがアサーションを使用していない場合でも、常にアサーションをオンにする必要があります。私のIDEは、新しい実行可能ファイルに対して常にデフォルトでこれを行うように設定されています。

  5. アサーションによって本番環境でのコードの動作が変更されることはないので、同僚はnullチェックが存在し、equals()メソッドにバグがある場合でもこのメソッドが適切に実行されることを嬉しく思います。equals()開発中にバグのあるメソッドを見つけられるので、私は幸せです。

また、失敗する一時的なアサーションを挿入することにより、アサーションポリシーをテストする必要があります。これにより、ログファイルまたは出力ストリームのスタックトレースを通じて通知されることを確認できます。


「バグの非表示」とアサートが開発中にバグを公開する方法に関する良い点!
nobar

これらのチェックはどれも遅いため、本番環境でオフにする理由はありません。「開発フェーズ」に現れない問題を検出できるように、ログステートメントに変換する必要があります。(とにかく、開発段階などというものはありません。コードの保守をまったくやめたとき、開発は終了します。)
Aleksandr Dubinsky

20

assertキーワードが何をするかを説明する良い答えはたくさんありますが、実際の質問に答える人はほとんどいませassertん。

答え:ほとんどありません

アサーションは、概念としては素晴らしいものです。良いコードがたくさんありif (...) throw ...ステートメント(およびその親戚のようなObjects.requireNonNullとはMath.addExact)。ただし、特定の設計上の決定により、assert キーワード自体の有用性が大幅に制限されています。

assertキーワードの背後にある原動力となるアイデアは時期尚早の最適化であり、主な機能はすべてのチェックを簡単にオフにできることです。実際、assertチェックはデフォルトでオフになっています。

ただし、本番環境では不変のチェックが引き続き行われることが非常に重要です。これは、完全なテストカバレッジは不可能であり、すべての製品コードには、アサーションが診断および軽減に役立つバグがあるためです。

したがって、の使用はif (...) throw ...、パブリックメソッドのパラメーター値のチェックやのスローに必要なのと同じように、推奨されますIllegalArgumentException

ときどき、処理に望ましくないほど長い時間がかかる(そして問題になるほど頻繁に呼び出される)不変のチェックを作成したくなることがあります。ただし、このようなチェックはテストの速度を低下させるため、これも望ましくありません。このような時間のかかるチェックは、通常、単体テストとして記述されます。それにもかかわらず、assertこの理由で使用することが理にかなっている場合があります。

assertそれがよりきれいできれいだからという理由で使用しないでくださいif (...) throw ...(そして私はきれいできれいなのが好きなので、非常に苦痛でそれを言います)。自分を助けることができず、アプリケーションの起動方法を制御できる場合は、自由に使用できますassert、本番環境では常にアサーションを有効にします。確かに、これは私がしがちなことです。私は原因となりますロンボク注釈を推進していますassertより多くのように動作しますif (...) throw ...ここに投票してください。

(ラント:JVM開発者はひどい時期尚早に最適化されたコーダーの集まりでした。そのため、JavaプラグインとJVMの非常に多くのセキュリティ問題について聞いたのです。彼らは本番コードに基本的なチェックとアサーションを含めることを拒否し、私たちは続けています価格を支払う。)


2
@aberglasキャッチオール句はcatch (Throwable t)です。などのOutOfMemoryError、AssertionErrorが、から回復/トラップ、ログ、または再試行しようとしない理由はない
アレクサンドル・ダビンスキー氏

1
OutOfMemoryErrorを検出して回復しました。
MiguelMunoz

1
同意しません。アサーションの多くは、APIが正しく呼び出されるようにするために使用されます。たとえば、オブジェクトがロックを保持している場合にのみ呼び出されるプライベートメソッドを作成する場合があります。別の開発者がオブジェクトをロックしないコードの一部からそのメソッドを呼び出す場合、アサーションはすぐに彼らが間違いを犯したことを知らせます。このような間違いがたくさんあり、確実に開発フェーズに巻き込まれる可能性があります。これらの場合、アサーションは非常に役立ちます。
MiguelMunoz

2
@MiguelMunoz私の答えでは、アサーションのアイデアは非常に良いと述べました。assertキーワードの実装が悪いです。概念ではなくキーワードを参照していることをより明確にするために、回答を編集します。
Aleksandr Dubinsky

2
例外ではなくAssertionErrorをスローするのが好きです。コードがIOExceptionのようなものしかスローできない場合は、例外をキャッチしてはならないことをまだ知らない開発者が多すぎます。誰かが例外をキャッチしたため、コードのバグが完全に飲み込まれてしまいました。アサーションはこのトラップに巻き込まれません。例外は、プロダクションコードで表示されることが予想される状況です。ロギングに関しては、エラーはまれですが、すべてのエラーもロギングする必要があります。たとえば、ログに記録せずにOutOfMemoryErrorを通過させますか?
MiguelMunoz 2017年

14

最も一般的な使用例を次に示します。列挙値をオンにするとします。

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;
}

4
そのため、警告を有効にし、警告をエラーとして扱う必要があります。中途半端なコンパイラーは、列挙型チェックが欠落していることを許可した場合のみ、コンパイル時にそうすることができます。これは、(おそらく、1日で)実行時。
Mike Nakis、2014

9
違法な引数の例外など、何らかの例外ではなく、ここでアサーションを使用するのはなぜですか?
liltitus27 2015年

4
これAssertionErrorにより、アサーションが有効な場合にがスローされ-eaます()。本番環境で望ましい動作は何ですか?サイレントノーオペレーションと潜在的な災害は、後で実行されますか?おそらく違います。私は明示的に提案するでしょうthrow new AssertionError("Missing enum value: " + fruit);
aioobe 2016年

1
AssertionErrorをスローするだけの場合に適した議論があります。本番環境での適切な動作については、主張の核心は、これが本番環境で発生しないようにすることです。アサーションは、バグをキャッチするための開発段階のツールであり、製品コードから簡単に削除できます。この場合、製品コードから削除する必要はありません。しかし、多くの場合、整合性テストは物事を遅くする可能性があります。プロダクションコードでは使用されないアサーション内にこれらのテストを配置することで、プロダクションコードの速度が低下することを心配することなく、完全なテストを自由に記述できます。
MiguelMunoz 2016年

これは間違っているようです。私見defaultでは、コンパイラーが欠落しているケースについて警告できるように、使用すべきではありません。あなたはできるreturnの代わりにbreak(これは、メソッドを抽出する必要がある場合があります)、その後、スイッチの後に不足している場合を扱います。このようにして、警告との両方の機会を得assertます。
maaartinus

12

アサーションは、事後条件と「失敗してはならない」事前条件をチェックするために使用されます。正しいコードはアサーションを失敗させるべきではありません。彼らがトリガーしたとき、彼らはバグを示すべきです(うまくいけば、問題の実際の場所がある場所に近い場所にあります)。

アサーションの例としては、メソッドの特定のグループが正しい順序で呼び出されていることを確認することが挙げられます(たとえば、それhasNext()がの前next()に呼び出されますIterator)。


1
next()の前にhasNext()を呼び出す必要はありません。
DJClayworth、

6
@DJClayworth:アサーションのトリガーを回避する必要もありません。:-)
ドナルフェロー

8

Javaのassertキーワードは何をしますか?

コンパイルされたバイトコードを見てみましょう。

私たちはそれを結論付けます:

public class Assert {
    public static void main(String[] args) {
        assert System.currentTimeMillis() == 0L;
    }
}

以下とほぼ同じバイトコードを生成します。

public class Assert {
    static final boolean $assertionsDisabled =
        !Assert.class.desiredAssertionStatus();
    public static void main(String[] args) {
        if (!$assertionsDisabled) {
            if (System.currentTimeMillis() != 0L) {
                throw new AssertionError();
            }
        }
    }
}

ここAssert.class.desiredAssertionStatus()true-eaはコマンドラインで渡される場合で、それ以外の場合はfalseです。

System.currentTimeMillis()最適化されないようにするために使用します(assert true;そうしました)。

合成フィールドが生成されるので、Java Assert.class.desiredAssertionStatus()はロード時に一度だけ呼び出す必要があり、その結果がそこにキャッシュされます。参照:「静的合成」の意味は何ですか?

次の方法で確認できます。

javac Assert.java
javap -c -constants -private -verbose Assert.class

Oracle JDK 1.8.0_45では、合成静的フィールドが生成されました(「静的合成」の意味は何ですか?):

static final boolean $assertionsDisabled;
  descriptor: Z
  flags: ACC_STATIC, ACC_FINAL, ACC_SYNTHETIC

静的初期化子と一緒に:

 0: ldc           #6                  // class Assert
 2: invokevirtual #7                  // Method java/lang Class.desiredAssertionStatus:()Z
 5: ifne          12
 8: iconst_1
 9: goto          13
12: iconst_0
13: putstatic     #2                  // Field $assertionsDisabled:Z
16: return

そして主な方法は:

 0: getstatic     #2                  // Field $assertionsDisabled:Z
 3: ifne          22
 6: invokestatic  #3                  // Method java/lang/System.currentTimeMillis:()J
 9: lconst_0
10: lcmp
11: ifeq          22
14: new           #4                  // class java/lang/AssertionError
17: dup
18: invokespecial #5                  // Method java/lang/AssertionError."<init>":()V
21: athrow
22: return

私たちはそれを結論付けます:

  • バイトコードレベルのサポートはありませんassert。これはJava言語の概念です
  • assertコマンドラインで-Pcom.me.assert=true置き換えるシステムプロパティ-eathrow new AssertionError()

2
したがって、catch (Throwable t)条項はアサーション違反もキャッチできますか?私にとって、その有用性はアサーションの本体に時間がかかるケースに限られています。
Evgeni Sergeev 2015年

1
それがアサーションの有用性を制限する理由がわかりません。非常にまれな場合を除いて、Throwableをキャッチしないでください。Throwableをキャッチする必要があるが、アサーションをキャッチしないようにしたい場合は、AssertionError最初のものをキャッチして再スローするだけです。
MiguelMunoz

7

Stackクラスからの実際の例(Java ArticlesのAssertionから)

public int pop() {
   // precondition
   assert !isEmpty() : "Stack is empty";
   return stack[--num];
}

80
これはCでは眉をひそめるでしょう。アサーションは絶対に起こらないはずです。空のスタックをポップすると、NoElementsExceptionまたは何かがスローされます。ドナルの返事を見てください。
Konerak

4
同意する。これは公式のチュートリアルから取ったものですが、悪い例です。
DJClayworth、

4
これにより、docs.oracle.com
Aram

7
おそらくメモリリークがあります。stack [num] = nullを設定する必要があります。GCがその仕事を適切に行うために。
H.Rabiee 2014

3
プライベートメソッドでは、クラスまたはメソッドの誤動作の例外があるのはおかしいので、アサーションを使用するのが正しいと思います。パブリックメソッドでは、外部のどこかから呼び出すと、他のコードがそれをどのように使用するかを実際に知ることはできません。isEmpty()を本当にチェックするのか?あなたは知りません。
Vlasec 2014

7

アサーションにより、コードの欠陥を検出できます。プログラムの運用中には、アサーションをオフにしたままテストとデバッグのアサーションをオンにすることができます。

それが真実であるとわかっているのに、なぜそれを主張するのですか?すべてが正しく機能している場合にのみ当てはまります。プログラムに欠陥がある場合、それは実際には真実ではない可能性があります。プロセスの早い段階でこれを検出すると、何かが間違っていることがわかります。

assert声明はオプションと一緒にこの文が含まれているStringメッセージ。

assertステートメントの構文には2つの形式があります。

assert boolean_expression;
assert boolean_expression: error_message;

アサーションを使用する場所と使用しない場所を管理するいくつかの基本的なルールを次に示します。アサーションは次の場合に使用する必要があります。

  1. プライベートメソッドの入力パラメーターを検証しています。パブリックメソッド用ではありませんpublicメソッドは、不正なパラメータが渡されたときに通常の例外をスローする必要があります。

  2. ほぼ間違いなく真実である事実の妥当性を保証するためのプログラムのどこか。

たとえば、1または2のどちらかだけであることが確実な場合は、次のようなアサーションを使用できます。

...
if (i == 1)    {
    ...
}
else if (i == 2)    {
    ...
} else {
    assert false : "cannot happen. i is " + i;
}
...
  1. メソッドの最後にポスト条件を検証します。つまり、ビジネスロジックを実行した後、アサーションを使用して、変数または結果の内部状態が期待どおりであることを確認できます。たとえば、ソケットまたはファイルを開くメソッドは、最後にアサーションを使用して、ソケットまたはファイルが実際に開かれていることを確認できます。

アサーションは次の場合には使用しないください

  1. パブリックメソッドの入力パラメーターを検証しています。アサーションは常に実行されるとは限らないため、通常の例外メカニズムを使用する必要があります。

  2. ユーザーが入力したものに対する制約の検証。同上。

  3. 副作用には使用しないでください。

たとえば、ここではアサーションがdoSomething()メソッドの呼び出しの副作用として使用されるため、これは適切な使用ではありません。

public boolean doSomething() {
...    
}
public void someMethod() {       
assert doSomething(); 
}

これが正当化される唯一のケースは、コードでアサーションが有効になっているかどうかを調べようとしている場合です。   

boolean enabled = false;    
assert enabled = true;    
if (enabled) {
    System.out.println("Assertions are enabled");
} else {
    System.out.println("Assertions are disabled");
}

5

ここで提供されるすべての優れた回答に加えて、公式のJava SE 7プログラミングガイドには、使用に関するかなり簡潔なマニュアルがありassertます。アサーションを使用するのが良い(そして重要なことに、悪い)アイデアであるときのいくつかの実例と、それが例外をスローすることとの違い

リンク


1
同意する。記事には多くの優れた例があります。特に、オブジェクトがロックを保持している場合にのみメソッドが呼び出されるようにする方法が気に入りました。
MiguelMunoz

4

アサートは開発時に非常に役立ちます。コードが正しく機能しているのに何か起こらない場合に使用します。使い方は簡単で、コード内にとどまることができます。実際にはオフになっているからです。

この状態が実際に発生する可能性がある場合は、対処する必要があります。

気に入っていますが、Eclipse / Android / ADTで有効にする方法がわかりません。デバッグ中でもオフのようです。(これにはスレッドがありますが、ADT実行構成には表示されない「Java vm」を指します)。


1
日食のIDEでアサーションを有効にするには、従ってくださいtutoringcenter.cs.usfca.edu/resources/...
Ayaz Pashaの

Androidでアサートをオンにする方法はないと思います。これは非常に残念です。
MiguelMunoz

3

これが、Hibernate / SQLプロジェクト用にサーバーに書き込んだアサーションです。エンティティBeanには、isActiveとisDefaultと呼ばれる2つの実質的にブール値のプロパティがありました。それぞれが「Y」、「N」、または「N」として扱われるnullの値を持つことができます。ブラウザクライアントがこれらの3つの値に制限されていることを確認します。したがって、これら2つのプロパティのセッターに、このアサーションを追加しました。

assert new HashSet<String>(Arrays.asList("Y", "N", null)).contains(value) : value;

次のことに注意してください。

  1. このアサーションは開発フェーズのみを対象としています。クライアントが不正な値を送信した場合、本番環境に到達するずっと前に、それを早期にキャッチして修正します。アサーションは、早期に検出できる欠陥に対するものです。

  2. このアサーションは遅く、非効率的です。大丈夫。アサーションは遅くてもかまいません。それらは開発専用のツールであるため、私たちは気にしません。アサーションが無効になるため、これによって本番コードが遅くなることはありません。(この点については不一致があります。これについては後で説明します。)これが私の次の点につながります。

  3. このアサーションには副作用はありません。変更不可能な静的な最終セットに対して自分の値をテストすることもできましたが、そのセットは本番環境に残り、使用されることはありませんでした。

  4. このアサーションは、クライアントの適切な操作を確認するために存在します。したがって、本番環境に到達するまでに、クライアントが適切に動作していることが確認されるため、安全にアサーションをオフにできます。

  5. 一部の人々はこれを尋ねます:アサーションが本番環境で必要ない場合、なぜ終わったらそれらを単に取り出しないのですか?次のバージョンに取り掛かるときにも必要になるからです。

一部の人々は、アサーションを使用すべきではないと主張しています。なぜなら、すべてのバグがなくなったことを確認することはできないため、本番環境でもバグを維持する必要があるからです。そして、assertステートメントを使用しても意味がありません。assertの唯一の利点は、それらをオフにできることです。したがって、この考え方によると、アサートは(ほとんど)使用しないでください。同意しません。テストが本番環境に属している場合は、アサートを使用しないでください。しかし、このテストは本番環境に属していませ。これは、本番環境に到達する可能性が低いバグをキャッチするためのものなので、完了したら安全にオフにすることができます。

ところで、私はそれをこのように書くことができたでしょう:

assert value == null || value.equals("Y") || value.equals("N") : value;

これは3つの値に対してのみ問題ありませんが、可能な値の数が増えると、HashSetバージョンがより便利になります。私は、効率性を強調するためにHashSetバージョンを選択しました。


このような小さな a HashSetを使用すると、に比べて速度が向上することを強く疑いArrayListます。さらに、セットとリストの作成がルックアップ時間を支配します。定数を使用する場合は問題ありません。それはすべて言った、+ 1。
maaartinus

すべて本当です。アサーションが自由に遅くなるという私のポイントを説明するために、私はこれを非効率的な方法で行いました。これはもっと効率的にすることもできますが、できないものもあります。"Writing Solid Code"と呼ばれる優れた本の中で、Steve MaguireはMicrosoft Excelのアサーションについて、変更すべきでないセルをスキップした新しいインクリメンタルアップデートコードをテストすることを述べています。ユーザーが変更を加えるたびに、アサーションはスプレッドシート全体を再計算して、結果が増分更新機能による結果と一致することを確認します。それは本当にデバッグバージョンを遅くしましたが、彼らはすべてのバグを早期にキャッチしました。
MiguelMunoz

完全に同意。アサーションは一種のテストです。アサーションは通常のテストほど用途が広くありませんが、プライベートメソッドをカバーでき、作成コストがはるかに安くなります。もっと使ってみます。
maaartinus

2

アサーションは基本的にアプリケーションのデバッグに使用されます。または、アプリケーションの有効性をチェックするために、一部のアプリケーションの例外処理の代わりに使用されます。

アサーションは実行時に機能します。概念全体を非常に簡単に説明できる簡単な例を以下に示します。assertキーワードはJavaで何をしますか?(WikiAnswers)。


2

アサーションはデフォルトで無効になっています。それらを有効にするには、-eaオプションを指定してプログラムを実行する必要があります(粒度は変更可能です)。たとえば、java -ea AssertionsDemo

アサーションを使用するための2つの形式があります。

  1. シンプル:例 assert 1==2; // This will raise an AssertionError
  2. Better:assert 1==2: "no way.. 1 is not equal to 2"; これは、指定されたメッセージも表示してAssertionErrorを発生させるため、より優れています。実際の構文ではassert expr1:expr2、expr2は値を返す任意の式にすることができますが、メッセージを出力するためだけに頻繁に使用しています。

1

要約すると(これはJavaだけでなく多くの言語にも当てはまります):

「アサート」は、主にソフトウェア開発者がデバッグプロセス中にデバッグ補助として使用します。アサートメッセージは表示されません。多くの言語では、「本番」コードの生成に使用するために、すべての「アサート」を無視するコンパイル時オプションを提供しています。

「例外」は、論理エラーを表すかどうかに関係なく、あらゆる種類のエラー条件を処理するための便利な方法です。なぜなら、続行できないようなエラー条件に遭遇した場合は、単に「空中に投げて、 「どこにいても、他の誰かがそれらを「キャッチ」する準備ができていることを期待しています。制御は、例外をスローしたコードからキャッチャーのミットに直接、1つのステップで転送されます。(そして、キャッチャーは行われた呼び出しの完全なバックトレースを見ることができます。)

さらに、そのサブルーチンの呼び出し元は、サブルーチンが成功したかどうかを確認する必要はありません「ここにいる場合は、成功している必要があります。それ以外の場合は、例外がスローされてここにいないからです!」この単純な戦略により、コードの設計とデバッグがはるかに簡単になります。

例外により、致命的なエラー条件を「ルールの例外」とすることができます。そして、それらが「ルールの例外... 「フライボール!」でもあるコードパスによって処理されるようにします


1

アサーションは、オフになる可能性のあるチェックです。それらはめったに使用されません。どうして?

  • それらは制御できないため、パブリックメソッドの引数のチェックに使用しないでください。
  • result != nullこのようなチェックは非常に高速であり、保存するものはほとんどないため、単純なチェックには使用しないでください。

だから、何が残っていますか?本当に真であると期待される状態の高価なチェック。良い例は、RBツリーのようなデータ構造の不変条件です。実際、JDK8では、このような意味のあるアサートがいくつかあります。ConcurrentHashMapTreeNodes

  • 実行時間を簡単に支配してしまう可能性があるため、本番環境ではそれらをオンに切り替えたくありません。
  • テスト中にそれらをオンまたはオフに切り替えることができます。
  • コードで作業するときは、必ずそれらをオンに切り替えます。

時々、小切手は本当に高価ではありませんが、同時に、あなたはかなり確かです、それは合格します。私のコードには、たとえば、

assert Sets.newHashSet(userIds).size() == userIds.size();

ここで、作成したリストに一意の要素があることは確かですが、文書化して再確認したかったのです。


0

基本的に、「trueをアサート」は成功し、「falseをアサート」は失敗します。これがどのように機能するかを見てみましょう:

public static void main(String[] args)
{
    String s1 = "Hello";
    assert checkInteger(s1);
}

private static boolean checkInteger(String s)
{
    try {
        Integer.parseInt(s);
        return true;
    }
    catch(Exception e)
    {
        return false;
    }
}

-8

assertキーワードです。JDK 1.4で導入されました。2種類がありassert、S

  1. 非常に単純なassertステートメント
  2. 単純なassertステートメント。

デフォルトでは、すべてのassertステートメントは実行されません。場合assert文が偽受け、それが自動的にアサーションエラーが発生します。


1
これは質問の目的である実際の例を提供していません
rubenafo

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