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

項目35 命名パターンよりアノテーションを選ぶ

今回からはアノテーションの話です。自作することはほとんどないし、よく使うアノテーションはそれほど多くないのでJavaの中でも覚えやすい部分です。

Java言語仕様の説明を直訳すると「プログラムの構成要素に情報を関連付けるマーカーであるが、実行時には効果がない」という感じでしょうか。

本書の第6章冒頭において、アノテーションはリリース1.5で追加された新たな種類のインタフェースと書かれていますが、言語仕様上でも通常のインタフェースとは別の種類のインタフェースと書かれていて、インタフェースと同じ章にその説明が記載されています。何にせよ、概要だけではどういったものか分かりませんね。

命名パターンの短所

昔はJUnitでテストメソッドを書く際に、メソッド名の頭は必ずtest~で始めるようにというルールがありました。testから始まるメソッドだけを実行する仕組みだったんですね。そういったメソッドの命名パターンで動作させるプログラムを決めるという手法には、いくつか短所があると書かれています。

1つ目の短所は、命名パターンを破るとそのメソッドが実行されずに正常終了することです。テスト実行者が、何かのメソッドが命名パターンを破ったかどうか気づくかどうかは分かりません。

2つ目の短所は、testから始まるメソッドを実行してくれるならば、testから始まるクラスを用意すればその中のメソッドを全部実行してくれるだろう、などという勝手な期待にはJUnitは答えてくれないということです。「適切なプログラム要素にだけそれらが使用されることを保証する方法がない」と本書には書かれています。

3つ目の短所は「適切なプログラム要素にパラメータを関連付ける良い方法を提供していない」とあります。特定の例外をスローした場合にだけ成功する種類のテストをサポートしたいと仮定してとあるので、仮にjava.io.FileNotFoundExceptionをスローした場合に成功するテストを行う場合を想定します。その場合はこんなテストメソッドを書くと思います。

スローされる例外をテストメソッド名に入れたところで、パラメータどおりの例外をチェックしているどうかはテストを実行するまで分かりません。例外を指定している文字列が本当に指定しているかを、コンパイラが検査する方法はないと書かれています。実際、上記のコードではコンパイラはそういったことをしません。

ちなみに上記のようなテストは、JUnit4ではこういう風に書けます(@Ruleを使う方がいいと思いますが、パラメータを使っていることが分かりやすいのでこの書き方にしてます)。

命名パターンの短所を挙げた後、その代わりとなるアノテーションの説明が続きます。

アノテーション型の定義

アノテーション型を自分で定義する場合、例えば以下のように書きます。例外がスローされたら失敗する単純なテストを指定するためのアノテーション型です。

アノテーション型の定義は「@interface アノテーション型名」の部分だけです。@interfaceという書き方から、アノテーションがインタフェースの一種であることが分かると思います。

もうサンプルに書かれていますが、定義したアノテーションを使用する場合は「@アノテーション型名(パラメータリスト)」として該当するプログラム構成要素の前に記述します。パラメータがなければカッコ以下は省略可です。

このサンプルではアノテーションの定義にさらにアノテーションが使用されています。こういったアノテーション型の宣言に対するアノテーションをメタアノテーションと呼んでいます。

まず「@Retention」ですが、これはアノテーションをどこまで保持するかどうかを指定するためのアノテーションです。@Retentionアノテーションのパラメータである、RetentionPolicy列挙の定数をまとめました。

表.RetentionPolicy列挙の定数
定数名 内容
CLASS アノテーションの情報はコンパイル時にクラスファイルに保持されるが、実行時には消去される
RUNTIME アノテーションの情報はコンパイル時にクラスファイルに保持されるが、実行時にも保持される。そのため、リフレクションを使ってアノテーション情報を読み取ることができる
SOURCE アノテーションの情報はソースにのみ保持され、コンパイル時には保持されない

上記のアノテーションのサンプルにおいて、なぜRetentionアノテーションのパラメータが「RetentionPolicy.RUNTIME」かというと、Testアノテーションが付いているメソッドだけ実行するテストランナーツールを作成したいからです。他のRetentionPolicyでは、実行時にアノテーション情報が取得できないため、Testアノテーションがついているかどうか分からなくなります。そのテストランナーの説明はおそらく次回やります。

次に「@Target」ですが、これはアノテーションが適用可能なプログラムの構成要素を指定します。例えばこのサンプルでは、メソッドにのみアノテーションを付けることができ、それ以外のクラスなどにTestアノテーションを記述するとコンパイルエラーになります。命名パターンの2つ目の短所がこの仕様により解決されています。TargetアノテーションのパラメータであるElementType列挙の定数は以下になります。

表.ElementType列挙の定数
定数名 構成要素
TYPE クラス、インタフェース(注釈型を含む)、またはenum宣言
FIELD フィールド宣言(enum定数を含む)
METHOD メソッド宣言
PARAMETER 仮パラメータ宣言
CONSTRUCTOR コンストラクタ宣言
LOCAL_VARIABLE ローカル変数宣言
ANNOTATION_TYPE 注釈型宣言
PACKAGE パッケージ宣言
TYPE_PARAMETER 型パラメータ宣言
TYPE_USE 型の使用(型を使うところならどこでも使える)

TYPE_PARAMETERとTYPE_USEについては、Java 8から追加された定数です。今回は以上、次回はアノテーションの使い方の話からとなります。

広告
  • LINEで送る