アノテーションを指定したい場合は、次のように記述します。パッケージ、クラス、インターフェイス、フィールド、メソッド、パラメータ、コンストラクタ、ローカル変数の任意の宣言中で修飾子として指定できます。
@アノテーション型名 ( アノテーション要素名 = 値 )
複数のアノテーション要素を指定する場合は、次のようにカンマで区切って指定します。
@アノテーション型名 ( アノテーション要素名 = 値, アノテーション要素名 = 値 )
初期値が指定されていないアノテーション要素については、初期化子(「アノテーション要素名 = 値」)の指定が必要です。アノテーション要素の指定順は自由です。
初期値がすべて指定されている場合や、そもそもアノテーション要素を持たないアノテーション型を使う場合には、初期化子は空で指定できます。sample22.app10.Aクラスのm1メソッドを見てください。
初期化子を空で指定できる場合は、そもそも初期化子の指定を省略できます。sample22.app10.Aクラスのm2メソッドを見てください。
package sample22.app10; public class A { @Deprecated() void m1() {} @Deprecated void m2() {} }
アノテーション型は、フルアノテーション型、単一値アノテーション型、マーカーアノテーション型、に分類できます。
単一値アノテーション型の例としては、先ほど紹介したSuppressWarningsがあります。このアノテーション型では、String[]型の要素valueが定義されています。String配列型なので、使用時には次のようにString配列を1つ指定できます。
@SuppressWarnings(value = {"serial","unchecked","deprecation"}) class A {}
配列の要素が1つしかないときは、初期化子には「value = {"unchecked"}」と指定すればいいのですが、配列の要素を囲む{}を省略できるようになっています。つまり、次のように書くことができます。
@SuppressWarnings(value = "unchecked") class B {}
実は、SuppressWarningsの要素は1つで名前がvalueなので、この場合は、要素名を省略できます。要素を1つしか持たないアノテーション型で、要素名がvalueでない場合は、このような要素名は省略できません。
@SuppressWarnings({"serial","unchecked","deprecation"}) class C {}
sample22.app11.Appクラスにこれらの例を入れておきました。サンプルなのでクラスへ一括で付けていますが、通常はもっと狭い範囲で付けます。また、実際には抑制すべきコンパイルエラーがないので、sample22.app11.Appクラス内の内部クラスに付けたアノテーションでは、コンパイラの警告が付きます。
アノテーションは、その用途に応じて、どのプログラム要素へ付けるかが決まります。例えば、ClassInformationはクラスに付けるものであり、メソッドへ付けても仕方がありません。このため、アノテーションを間違ったプログラム要素へ付けないように気を付ける必要があります。
Javaでは、アノテーション型にTargetアノテーションを付けることで、こういった間違ったアノテーション型の利用をチェックできます。ちなみに、java.lang.annotation.Targetアノテーション型のように、アノーテション型に付けるアノーテションのことを「メタアノテーション(meta-annotation)」と言います。
メソッド情報用のアノテーションとしてMethodInformationアノテーション型を用意したとします。Targetアノテーション型は標準で用意されているアノテーションで、java.lang.annotation.ElementType型配列だけを要素に持ち、その要素名はvalueです。
ElementType型はプログラム要素の種類を表すenum型で、「ANNOTATION_TYPE」「CONSTRUCTOR」「FIELD」「LOCAL_VARIABLE」「METHOD」「PACKAGE」「PARAMETER」「TYPE」といった定数が定義されています。名前から意味は分かるはずです。TYPEはクラス、インターフェイス(アノテーションを含みます)、enum型の宣言と対応します。
これらを使って、次のように指定します。ElementType.METHODを指定することにより、MethodInformationアノテーションはメソッドにだけ付けられます。
package sample22.app12; import java.lang.annotation.ElementType; import java.lang.annotation.Target; @Target(ElementType.METHOD) public @interface MethodInformation { }
ClassInformationアノテーションはクラスに付けるので、ElementType.TYPEを指定すればいいということです。
package sample22.app12; import java.lang.annotation.ElementType; import java.lang.annotation.Target; @Target(ElementType.TYPE) public @interface ClassInformation { String author(); double version() default 1.0; String dependon(); }
これまでアノテーションを参照するのは開発者だけではなく、EclipseやJavaコンパイラなどの開発用プログラムも参照するということを説明しました。
このことから分かるように、プログラムがアノテーションを参照できると便利な場面が多いため、アノテーションをプログラム実行時にも利用できるようにしたいことがあります。一方で、プログラム実行時には不要なアノテーションもあり、プログラムサイズを小さくしたい場合には、情報が残っていると邪魔になることもあります。
そのため、指定したアノテーションについて、「どのタイミングでアクセスができるようにしておくか」を、アノテーション型を定義するときに指定できるようになっています。
具体的には、java.lang.annotation.Retentionアノテーション型を使います。指定できる値はjava.lang.annotation.RetentionPolicy型で定義されています。これはenum型で、次の3つの定数があります。
java.lang.annotation.Retentionアノテーションを指定しない場合は、java.lang.annotation.RetentionPolicy.CLASSが適用されます。ただし、ローカル変数に対するアノテーションは、コンパイラに破棄されます。
Javaではアノテーション型を使うことで、ソースコードへアノテーションを埋め込めます。アノテーションを使うことにより、コンパイラやプログラム実行環境へプログラムについての付加情報を伝えて特別な動作ができることを理解できたでしょうか。
java.langパッケージに含まれるアノテーション型、Deprecated、Override、SuppressWarningsは使えるようにしておきましょう。java.lang.annotationパッケージのTarget、Retentionについては、最初はそういうものがあると知っていれば十分です。
この他にも、今回は説明していませんが、java.lang.annotationパッケージに含まれる、「Documented」「Inherited」といったものもあり、JDK 6からはたくさんのアノテーションが追加されています。興味のある方は、「java.lang.annotation (Java Platform SE 6)のAPIリファレンス」などで調べてみてください。
本記事では、Javadocコメントとメタデータについて説明したうえで、Java標準アノテーション型の中でも重要なDeprecated、Override、SuppressWarningsについて、基本的な使い方を説明しました。また、独自のアノテーション型を定義したり利用したりする方法について説明しました。最近の実用的なJava用ライブラリではアノテーションの使用を前提としたものも多いので、使えるようにしておきましょう。
今回作ったサンプルのソースコードはこちらからダウンロードできます。
小山博史(こやま ひろし)
情報家電、コンピュータと教育の研究に従事する傍ら、オープンソースソフトウェア、Java技術の普及のための活動を行っている。長野県の地域コミュニティである、SSS(G)やbugs(J)の活動へも参加している。
著書に「基礎Java」(インプレス)、共著に「Javaコレクションフレームワーク」(ソフトバンククリエイティブ)、そのほかに雑誌執筆多数。
Copyright © ITmedia, Inc. All Rights Reserved.