Effective Javaを読むチャレンジ-項目35 その3-

パラメータを持つアノテーション型

特定の例外がスローされた場合にだけ成功するテストのために、新しいアノテーションを作ります。

メタアノテーションについては前回のTestアノテーションと変わりませんが、valueメソッドという型メンバを持っています。

言語仕様は複雑なので簡単に省略して書くと、アノテーション型のメンバは「型名 識別子()」という記述になります。これはメソッドのように使いますが、引数を持てない制限が付いています。カッコの中に引数を定義するとコンパイルエラーになります。

その型メンバの識別子が、アノテーションのパラメータ名になります。このExceptionTestアノテーションでは「value」という名前のパラメータを持つことになります。本書ではこのClassの型パラメータについて解説してあります。ご覧のとおり、この型パラメータは「Exceptionを拡張した何らかのクラスに対するClassオブジェクト」となり、境界型トークンの使用例でもあります。

このアノテーションを使ったサンプルが以下となります。

アノテーション型のメンバが1つだけの場合、「@アノテーション型(パラメータ名=値)」と記述して使用します。メンバが1つの場合、かつパラメータ名が「value」のときのみパラメータ名を省略できるようです。たとえば「id」という識別子のメンバ1つのみの場合に省略しようとするとコンパイルエラーが出ました。Wikipediaを見ると、単一のメンバを持つ場合はvalueという名前にするのが儀礼であると書かれていました。単一メンバの場合は「value」という名前にするとパラメータ名を省略できて使いやすくなるので、そうすべきだと思います。

次に、このサンプルに対して実行するテストランナーツールを、前回のテストランナーを修正して作成します。

実行結果はこうなります。

「m.getAnnotation(ExceptionTest.class).value()」の部分で、該当するアノテーションのパラメータを取得しています。このvalueメソッドは、ExceptionTestアノテーションのメンバです。メンバの名前が「id」の場合、このコードは「~.id()」となります。

次に「excType.isInstance(exc)」の部分ですが、isInstanceメソッドは引数であるオブジェクトが指定したオブジェクトに代入可能かどうかを調べます。上記の場合でいうと「テスト対象のメソッドからスローされた例外オブジェクトが、期待値であるExceptionTestアノテーションのパラメータで定義した例外クラスのオブジェクトに代入可能か」どうかを調べています。

しかしながら、このテストランナーツールでは、複数の例外をスローする可能性のあるメソッドのテストはできません。同じ型のアノテーションは、複数重ねて使うことはできません。ということで、アノテーション型のパラメータに複数の値を設定できるようにします。その結果、こうなります。

このようにメンバを配列にすることで、パラメータに複数の値を持たせることができます。使用する場合はこうなります。

基本的に配列の定義のように{}で囲み、カンマ区切りで値を連ねます。値が1つの場合は{}を省略できます。本書で言われているとおり、柔軟にできています。これで命名パターンの3つ目の短所についての改善策もできました。

結論として、今はアノテーションがあるので、命名パターンを使うのは論外だと言われています。そして、すべてのプログラマは事前に定義されているアノテーション型を使用すべきだと言われています。ツール開発者でもない限り自作する必要はないでしょう、ということですね。今回のようなテストに関する命名パターンの代わりはJUnit4などのテストフレームワークで用意された仕組みを使うべきですし。

ということで、項目35が読み終わりました。

広告
  • LINEで送る