プロダクションコードのassertステートメントでnullでないことをアサートする必要がありますか?[閉まっている]


32

この質問を見ことがありますが、assertキーワードの使用法についていくつか質問があります。の使用について他の数人のコーダーと議論していましたassert。この使用例では、特定の前提条件が満たされている場合にnullを返すことができるメソッドがありました。私が書いたコードはメソッドを呼び出し、それがnullを返さないことを表明し、返されたオブジェクトを引き続き使用しています。

例:

class CustomObject {
    private Object object;

    @Nullable
    public Object getObject() {
        return (object == null) ? generateObject() : object;
    }
}

次のように使用するとします。

public void useObject(CustomObject customObject) {
    object = customObject.getObject();
    assert object != null;
    // Do stuff using object, which would throw a NPE if object is null.
}

を削除する必要があると言われましassertた。これらは製品コードでは使用しないでください。テストでのみ使用してください。本当?


19
デフォルトでは、アサーションは無効になっています。を介して、-eaまたは同等のものを実行時に明示的に有効にする必要があります。
jarmod

ソフトウェアエンジニアリングに関する関連質問:softwareengineering.stackexchange.com/q/137158/187318(かなり古いので、Objects.requireNonNullJava 8での導入以降、不要な外部ライブラリが推奨されています)。
ハルク

この質問のタイトルは非常に広範ですが、本文に記載されている質問ははるかに狭く、回答ごとにヘルパー関数によって完全に処理されます。
smci

明確にするために、クライアントコードがオブジェクトにnull以外のチェック/アサートを実装する必要があるかどうかを尋ねています。(これは、ライブラリコード自体がオブジェクトをnullにできないことを保証する必要があるかどうか、またはnullであることをアサートする必要があるかどうかを確認することとは異なります)。
smci

回答:


19

Objects.requireNonNull(Object)そのために使用します。

指定されたオブジェクト参照がnullではないことを確認します。このメソッドは、主にメソッドとコンストラクターでパラメーターの検証を行うために設計されています[...]

あなたの場合、これは:

public void useObject(CustomObject customObject) {
    object = customObject.getObject();
    Objects.requireNonNull(object);
    // Do stuff using object, which would throw a NPE if object is null.
}

この関数は、あなたが言及した目的のために作られています。つまり、nullにはならないものを明示的にマークします。また、生産中。大きな利点は、最初から発生してはならない場所でnull値を見つけることができることです。本来あるべきではない場所に渡されたnull値によって引き起こされる問題のデバッグの問題が少なくなります。

もう1つの利点は、とは対照的に、nullチェックに関する追加の柔軟性assertです。ながら、assertブール値をチェックするためのキーワードであり、Objects.requireNonNull(Object)関数であり、したがって、はるかに柔軟で読み取り可能なコードに埋め込むことができます。例えば:

Foo foo = Objects.requireNonNull(service.fetchFoo());

// You cannot write it in on line.
Bar bar = service.fetchBar();
assert bar != null;
service.foo(Objects.requireNonNull(service.getBar()));

// You cannot write it in on line.
Bar bar = service.getBar();
assert bar != null;
service.foo(bar);

覚えておいてくださいObjects.requireNonNull(Object)ヌルチェックのためだけにあるassert一般化されます。assertつまり、主にテストするという点で、目的は少し異なります。これを有効にする必要があるため、テスト用に有効にして、本番用に無効にすることができます。とにかく、製品コードには使用しないでください。テスト用の不要で複雑な検証により、アプリケーションが遅くなる可能性があります。これを使用して、テスト専用のチェックを本番用のチェックから分離します。

の詳細については、公式ドキュメントをご覧くださいassert


14
誰がいつ宣言されたのかは、遺産だと主張しますか?リンク/参照はありますか?テストで簡単に有効化し、本番環境で無効化できるゼロフットプリント検証を可能にするツールを「レガシー」と定義する正気の開発者は想像できません。
Dmitry Pisklov

実行時にアサートが動作するかどうかを決定できても、NPEがrequireNonNullによってスローされるかどうかを実行時に判断できない場合、「アサートよりも制御が強化されている」とはどういう意味ですか?
ピートカーカム

@DmitryPisklovあなたは正しい、私はそれを編集した。テストに最適なツールです。
akuzminykh

2
@PeteKirkhamあなたは正しい、コントロールはそのための間違った言葉だった。構文の柔軟性についてもっと話していました。また:ここにあなたがコードがのNPEをスローしたいと思う理由を見つけることができます。
akuzminykh

1
「本番用のコードには使用しないでください。アプリケーションの速度が低下する可能性があります」できるかもしれませんが、それだけの価値はあります。Javaには、配列をオーバーランしないようにするための組み込みのランタイムチェックがあります。これらは、パフォーマンスが低下する可能性があるにもかかわらず、本番環境での導入でも有効になります。私たちはそれについての選択さえ与えられていません。本番用コードでランタイムチェックを行うことは必ずしも間違っているとは限りません。
Max Barraclough

22

アサーションについて覚えておくべき最も重要なことは、アサーションを無効にできることです。そのため、アサーションが実行されると想定しないでください。

下位互換性のために、JVMはデフォルトでアサーション検証を無効にしています。-enableassertionsコマンドライン引数、またはその省略形-eaを使用して明示的に有効にする必要があります。

java -ea com.whatever.assertion.Assertion

したがって、それらに依存することは良い習慣ではありません。

アサーションはデフォルトで有効になっていないため、コードで使用されたときに実行されるとは決して想定できません。したがって、常にnull値と空のOptionalsをチェックし、アサーションを使用してpublicメソッドへの入力をチェックすることを避け、代わりに未チェックの例外を使用する必要があります。一般に、アサーションが存在しないかのようにすべてのチェックを実行します。


それらに依存するのは明らかに悪い習慣ですが、一般的にそれを使用するのは悪い習慣ですか?
Big_Bad_E

3
@Big_Bad_Eアサーションは、プログラムをできるだけ早くそしてうるさく失敗させるために存在するデバッグツールです。その場合、アサーションにもかかわらずプログラムが失敗する可能性がないことを確認するのがテストスイートの仕事です。アイデアは、テストスイート(100%のカバレッジを取得しましたが、そうではありませんか?!?)が失敗することなく実行されたら、アサーションをトリガーできなくなるため、アサーションを削除しても問題ありません。確かに、この推論には少し理想主義がありますが、それが考え方です。
cmaster-

11

きっとあなたが言われるのは露骨な嘘です。これが理由です。

スタンドアロンのjvmを実行するだけの場合、アサーションはデフォルトで無効になっています。無効にすると、フットプリントがゼロになるため、本番アプリケーションには影響しません。ただし、コードを開発およびテストするときはおそらく彼らはあなたの親友であり、ほとんどのテストフレームワークランナーはアサーションを有効にするため(JUnitが行う)、ユニットテストを実行するとアサーションコードが実行され、潜在的なバグを早期に検出するのに役立ちます(例:一部のビジネスロジック境界チェックにアサートを追加できます。これは、不適切な値を使用するコードの検出に役立ちます)。

とは言っても、他の回答が示唆しているように、その理由から(常に有効になっているとは限らない)、重要なチェックを行うためにアサーションに依存したり、(特に)状態を維持したりすることはできません。

アサートの使用方法の興味深い例については、こちらをご覧ください。ファイルの最後に、singleThreadedAccess()201行目のassertステートメントから呼び出され、テストで潜在的なマルチスレッドアクセスをキャッチするメソッドがあります。


4

他の回答はすでに十分にこれをカバーしていますが、他のオプションがあります。

たとえば、Springには静的メソッドがあります。

org.springframework.util.Assert.notNull(obj)

独自のAssert.something()メソッドを持つ他のライブラリもあります。自分で書くのもとても簡単です。

ただし、これがWebサービスの場合、どの例外をスローするかを覚えておいてください。たとえば、前述のメソッドは、IllegalArgumentExceptionデフォルトで500を返すanthrowをスローします。

Webサービスの場合、これは多くの場合、内部サーバーエラーではなく、500ではなく400である必要があります。これは不適切な要求です。


1
「アサート」メソッドがプロセスをすぐにクラッシュせず、代わりにある種の例外をスローする場合、それはアサートではありません。重要なのassertは、生成されたコアファイルを使用して即時にクラッシュし、お気に入りのデバッガーを使用して、条件に違反した場所で事後分析を行えるようにすることです。
cmaster-

アサートはすぐにプロセスをクラッシュさせません。キャッチされる可能性のあるエラーがスローされます。未処理の例外は、エラーだけでなくテストもクラッシュさせます。必要に応じて、セマンティクスを主張できます。必要に応じて、例外ではなくエラーをスローするカスタムアサートを記述します。実際には、圧倒的多数のケースで、適切なアクションはエラーではなく例外をスローすることです。
クリストファーシュナイダー

ああ、Javaは確かassertにスローするように定義しています。面白い。C / C ++バージョンはそのようなことをしません。これは、a)プロセスを強制終了し、b)コアダンプを作成するというシグナルをすぐに発生させます。これには理由があります。コールスタックに関するすべての情報がまだ利用できるため、このようなアサーションエラーをデバッグするのは非常に簡単です。assert代わりに例外をスローするように定義すると、プログラムによって意図的に(非)キャッチされる可能性があり、目的を無効にします。
cmaster-

CおよびC ++にいくつか(または多く)の奇妙なことがあるように、Javaにもいくつかあります。それらの1つですThrowable。彼らは常に捕まることができます。それが良いことなのかどうかはわかりません。場合によります。多くのJavaプログラムはWebサービスであり、ほとんどの理由でクラッシュすることは望ましくないため、ほぼすべてのものがキャッチされ、ログに記録されます。優れた点の1つはスタックトレースで、通常はそれだけで例外またはエラーの理由を診断するのに十分です。
クリストファーシュナイダー

3

Useは、プログラミングの間違い、つまりバグを見つけるのに役立ちます。

assertを使用して、論理的に発生する可能性のあるもの、つまり不適切にフォーマットされた入力をキャッチしないでください。エラーが回復不可能な場合にのみ、assertを使用します。

アサーションのチェック時に実行されるコードには、プロダクションロジックを含めないでください。ソフトウェアが適切に記述されている場合、これはほんとうに真実ですが、そうでない場合、アサーションを有効または無効にすると、微妙な副作用と異なる全体的な動作が発生する可能性があります。

会社が「テストコード」と「プロダクションコード」で同じことを行っているが、異なるコードベース(または編集の異なる段階)で作業している場合は、そこから抜け出して二度と戻らないでください。そのレベルの無能を修正しようとすることは、おそらくあなたの時間の無駄です。会社がアサートステートメントをテストのコードの外側に配置していない場合は、本番ビルドでアサートが無効になっていることを伝えてください。そうでない場合は、その間違いを修正することが最優先事項です。

アサートの値は、テストスイートだけでなく、ビジネスロジック内で正確に使用されます。これにより、コードの大きなチャンクを通過してこれらのすべてのアサーションをトリガーするために多くのものを明示的にテストする必要のない多くの高レベルのテストを簡単に作成できます。いくつかのプロジェクトでは、典型的なテストは実際には何もアサートしませんでした。特定の入力に基づいて計算を実行するように命令しただけで、数百のアサーションがチェックされ、小さなロジック部分でも問題が見つかりました。


2

アサートはいつでも使用できます。議論は来るべき時です。たとえば、ガイドでは

  • パブリックメソッドの引数チェックにアサーションを使用しないでください。
  • アサーションを使用して、アプリケーションが正しく動作するために必要な作業を行わないでください。

4
良いルール。しかし、答えはいくつかの理由が追加された方が良いでしょう。
cmaster-モニカを
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.