Javaでアサーションを使用する方法

実行時に正しく機能するプログラムを作成するのは難しい場合があります。これは、実行時にコードがどのように動作するかについての仮定が間違っていることが多いためです。Javaのアサーション機能を使用することは、プログラミングロジックが適切であることを確認する1つの方法です。

このチュートリアルでは、Javaアサーションを紹介します。最初に、アサーションとは何か、およびそれらをコードで指定して使用する方法を学習します。次に、アサーションを使用して前提条件と事後条件を適用する方法を説明します。最後に、アサーションと例外を比較し、コードで両方が必要な理由を確認します。

ダウンロードコードを入手するこのチュートリアルの例のソースコードをダウンロードします。JavaWorld用にJeffFriesenによって作成されました。

Javaアサーションとは何ですか?

JDK 1.4より前は、開発者はコメントを使用して、プログラムの正当性に関する仮定を文書化することがよくありました。ただし、コメントは、仮定をテストおよびデバッグするためのメカニズムとしては役に立ちません。コンパイラはコメントを無視するため、バグ検出にコメントを使用する方法はありません。また、開発者はコードを変更するときにコメントを更新しないことがよくあります。  

JDK 1.4では、コードに関する仮定をテストおよびデバッグするための新しいメカニズムとしてアサーションが導入されました。本質的に、アサーション は、プログラムテスト用に有効にしたと仮定して、実行時に実行されるコンパイル可能なエンティティです。アサーションをプログラムして、バグが発生した場所にバグを通知することができます。これにより、失敗したプログラムのデバッグに費やす時間を大幅に短縮できます。

アサーションは、条件(ブール式)の真の値をテストし、そのような条件が偽の場合に開発者に通知することにより、プログラムを正しくするかどうかを決定する要件を体系化するために使用されます。アサーションを使用すると、コードの正確性に対する信頼を大幅に高めることができます。

Javaでアサーションを書く方法

アサーションは、assertステートメントとjava.lang.AssertionErrorクラスを介して実装されます。このステートメントはキーワードで始まりassert、ブール式で続きます。構文的には次のように表されます。

BooleanExprをアサートします;

場合はBooleanExprtrueと評価され、何も起こりませんし、実行が継続されます。ただし、式がfalseと評価された場合はAssertionError、リスト1に示すように、インスタンス化されてスローされます。

リスト1 :(AssertDemo.javaバージョン1)

public class AssertDemo {public static void main(String [] args){int x = -1; x> = 0をアサートします。}}

リスト1のアサーションは、変数xに0以上の値が含まれているという開発者の信念を示しています。ただし、これは明らかに当てはまりません。assertスローでのステートメントの実行結果AssertionError

リスト1(javac AssertDemo.java)をコンパイルし、アサーションを有効にして実行します(java -ea AssertDemo)。次の出力を確認する必要があります。

AssertDemo.main(AssertDemo.java:6)でのスレッド "main" java.lang.AssertionErrorの例外

このメッセージは、AssertionErrorがスローされた原因を特定できないという点で、やや不可解です。より有益なメッセージが必要な場合は、assert以下のステートメントを使用してください。

BooleanExprをアサートしますexpr ;

ここに、expr値を返すことができる式(メソッド呼び出しを含む)があります—戻り値のvoid型でメソッドを呼び出すことはできません。リスト2に示すように、有用な式は失敗の理由を説明する文字列リテラルです。

リスト2 :(AssertDemo.javaバージョン2)

public class AssertDemo {public static void main(String [] args){int x = -1; アサートx> = 0: "x <0"; }}

リスト2(javac AssertDemo.java)をコンパイルし、アサーションを有効にして実行します(java -ea AssertDemo)。今回は、スローされた理由を含む、次のわずかに拡張された出力を確認する必要がありますAssertionError

スレッド "main" java.lang.AssertionErrorの例外:AssertDemo.main(AssertDemo.java:6)でx <0

どちらの例でAssertDemoも、-ea(アサーションを有効にする)オプションなしで実行すると、出力はありません。アサーションが有効になっていない場合、クラスファイルにはまだ存在しますが、アサーションは実行されません。

前提条件と事後条件

アサーションは、プログラムのさまざまな前提条件と事後条件に違反していないことを確認することでプログラムの前提条件をテストし、違反が発生したときに開発者に警告します。

  • 前提条件は、いくつかのコード・シーケンスの実行前に真と評価されなければならない状態です。前提条件は、発信者が着信者との契約を維持することを保証します。
  • 事後条件は、いくつかのコード・シーケンスの実行後に真と評価されなければならない状態です。事後条件により、呼び出し先は呼び出し元との契約を維持できます。

前提条件

明示的なチェックを行い、必要に応じて例外をスローすることで、パブリックコンストラクターとメソッドに前提条件を適用できます。プライベートヘルパーメソッドの場合、アサーションを指定することで前提条件を適用できます。リスト3を検討してください。

リスト3 :(AssertDemo.javaバージョン3)

import java.io.FileInputStream; インポートjava.io.InputStream; インポートjava.io.IOException; class PNG {/ ** * PNGインスタンスを作成し、指定されたPNGファイルを読み取り、*適切な構造にデコードします。* * @paramfilespecパスと読み取るPNGファイルの名前** @throws NullPointerException when filespecis *null* / PNG(String filespec)throws IOException {//非プライベートコンストラクターと//メソッドに前提条件を適用します。 if(filespec == null)throw new NullPointerException( "filespec is null"); try(FileInputStream fis = new FileInputStream(filespec)){readHeader(fis); }} private void readHeader(InputStream is)throws IOException {//プライベートヘルパーメソッドで前提条件が満たされていることを//確認します。 assert is!= null: "nullがisに渡されました"; }} public class AssertDemo {public static void main(String [] args)throws IOException {PNG png = new PNG((args.length == 0)?null:args [0]); }}

PNGリスト3のクラスは、PNG(ポータブルネットワークグラフィックス)画像ファイルを読み取ってデコードするためのライブラリの最小限の始ま​​りです。コンストラクターは明示的にと比較filespecnullNullPointerExceptionこのパラメーターにが含まれてnullいる場合にスローします。重要なのは、をfilespec含まない前提条件を適用することnullです。

assert filespec != null;アサーションが無効になっていると、コンストラクタのJavadocに記載されている前提条件が(技術的に)尊重されないため、指定することは適切ではありません。(実際には、FileInputStream()がスローされるため、それは尊重NullPointerExceptionされますが、文書化されていない動作に依存するべきではありません。)

ただし、assertプライベートreadHeader()ヘルパーメソッドのコンテキストでは適切です。プライベートヘルパーメソッドは、PNGファイルの8バイトヘッダーを読み取ってデコードするために最終的に完了します。is常にnull以外の値が渡されるという前提条件が常に保持されます。

事後条件

事後条件は通常、メソッド(またはコンストラクター)がパブリックであるかどうかに関係なく、アサーションを介して指定されます。リスト4を検討してください。

リスト4 :(AssertDemo.javaバージョン4)

public class AssertDemo {public static void main(String [] args){int [] array = {20、91、-6、16、0、7、51、42、3、1}; sort(array); for(int element:array)System.out.printf( "%d"、element); System.out.println(); } private static boolean isSorted(int [] x){for(int i = 0; ix [i + 1])return false; trueを返します。 } private static void sort(int [] x){int j、a; //左端の値を除くすべての整数値の場合... for(int i = 1; i 0 && x [j-1]> a){//左シフト値--x [j-1] -1つの位置その右側-// x [j]。 x [j] = x [j-1]; //挿入位置をシフトされた値の元の位置に更新します//(1つ左の位置)。 j--; } //挿入位置(最初の挿入位置または最後の挿入位置のいずれか)にaを挿入します。ここで、aは//またはその左側のすべての値と同じです。 x [j] = a;} assert isSorted(x): "配列がソートされていません"; }}

リスト4sort()は、挿入ソートアルゴリズムを使用して整数値の配列をソートするヘルパーメソッドを示しています。私は、呼び出し元に戻る前assertx、ソートされた後の状態をチェックするために使用しましたsort()

リスト4の例は、アサーションの重要な特性を示しています。これは、通常、実行にコストがかかることです。このため、通常、アサーションは本番コードでは無効になっています。リスト4では、isSorted()配列全体をスキャンする必要があります。これは、配列が長い場合は時間がかかる可能性があります。

Javaでのアサーションと例外

開発者はアサーションを使用して、論理的に不可能な状況を文書化し、プログラミングロジックのエラーを検出します。実行時に、有効なアサーションは開発者に論理エラーを警告します。開発者は、ソースコードをリファクタリングして論理エラーを修正してから、このコードを再コンパイルします。

開発者は、Javaの例外メカニズムを使用して、致命的ではない(メモリ不足など)ランタイムエラーに応答します。これは、ファイルが存在しないなどの環境要因、または除算の試みなどの不十分なコードによって引き起こされる可能性があります。 0.プログラムの実行を継続できるように、エラーから正常に回復するために例外ハンドラーが作成されることがよくあります。

Assertions are no substitute for exceptions. Unlike exceptions, assertions don’t support error recovery (assertions typically halt program execution immediately — AssertionError isn’t meant to be caught); they are often disabled in production code; and they typically don’t display user-friendly error messages (although this isn’t an issue with assert). It’s important to know when to use exceptions rather than assertions.

When to use exceptions

Suppose you’ve written a sqrt() method that calculates the square root of its argument. In a non-complex number context, it’s impossible to take the square root of a negative number. Therefore, you use an assertion to fail the method if the argument is negative. Consider the following code fragment:

public double sqrt(double x) { assert x >= 0 : "x is negative"; // ... }

It’s inappropriate to use an assertion to validate an argument in this public method. An assertion is intended to detect errors in programming logic and not to safeguard a method from erroneous arguments. Besides, if assertions are disabled, there is no way to deal with the problem of a negative argument. It’s better to throw an exception, as follows:

public double sqrt(double x) { if (x < 0) throw new IllegalArgumentException("x is negative"); // ... }

The developer might choose to have the program handle the illegal argument exception, or simply propagate it out of the program where an error message is displayed by the tool that runs the program. Upon reading the error message, the developer can fix whatever code led to the exception.

アサーションとエラー検出ロジックの微妙な違いに気づいたかもしれません。アサーションはテストx >= 0しますが、エラー検出ロジックはテストしx < 0ます。アサーションは楽観的です。引数はOKであると想定しています。対照的に、エラー検出ロジックは悲観的です。引数がOKではないと仮定します。アサーションは正しいロジックを文書化しますが、例外は誤った実行時の動作を文書化します。

このチュートリアルでは、アサーションを使用して正しいプログラムロジックを文書化する方法を学習しました。また、アサーションが例外の代わりにならない理由を学び、例外を使用する方が効果的な例を見てきました。

このストーリー「Javaでアサーションを使用する方法」は、もともとJavaWorldによって公開されました。