The Java TutorialsのType Annotations and Pluggable Type Systemsのセクションを読んで訳した。
Type Annotations and Pluggable Type Systems
Java SE 8 release以前では、アノテーションは宣言にのみ付与可能でした。Java SE 8 release以降では、アノテーションは型が使用される場所ならどこでも(any type use)付与可能です。この意味するところは、アノテーションは型を使う場所でならどこででも使用可能、ということです。型が使用される場所の例としては、new
式でのインスタンス生成・キャスト・implements
節・throws
節、などです。アノテーションのこの形式はtype annotationと呼ばれ、いくつかの例をAnnotations Basicsに載せています。
Type annotationsは強力な型チェックを保証するJavaプログラムの解析方法を改良するために開発されました。Java SE 8 releaseは型チェックフレームワークを提供しませんが、型チェックフレームワークを開発(もしくはダウンロード)することが出来ます。型チェックフレームワークとは、Javaコンパイラと共に使用する一つ以上のpluggable modulesとして実装されるものです。
たとえば、プログラム内の個々の変数にnullを決して代入させないようにしたい、とします。つまり、NullPointerException
の発生を避けたい、という意味です。この目的をチェックするためのカスタムプラグインを作ることができます。個々の変数に決してnullが代入されないことを指示するアノテーションを付与するコードを、既存のコードに追加します。変数宣言は以下のようになります。
@NonNull String str;
このコードをコマンドラインでNonNull
を含めてコンパイルするとき、コンパイラは潜在的な問題を発見した場合にはwarningを表示します。エラーを回避するにはその部分のコードを修正します。すべてのwarningを取り除けば、個々のエラーはプログラム実行時には発生しないことになります。
それぞれのモジュールが異なる種類のエラーをチェックする、複数の型チェックモジュールを使用できます。この方法では、Javaの型システムのトップレベルにおいて、ユーザの望む時と場所で指定のチェックを加えて、ビルドをすることができます。
pluggable type checkersの存在とtype annotationsの適切な利用をすれば、エラーになりがちな事象を減らしつつ、強力なコードを書くことが出来ます。
多くの場合では、自前の型チェックモジュールを開発する必要はありません。サードバーティー製品が存在します。たとえば、ワシントン大学が開発したChecker Frameworkを活用できます。このフレームワークには、NonNull
モジュール・正規表現と同等のモジュール・mutex lockモジュール、が含まれています。詳細については、The Checker Frameworkを参照してください。
Repeating Annotations
場合によっては型の使用場所や定義に複数のアノテーションを付与したいことがあります。Java SE 8 release以降では、repeating annotations
によって可能になります。
たとえば、ある種のスケジュールや与えられた時間にメソッドを実行する、UNIX cron
サービスのような、タイマーサービスを使用するコードを書いている、とします。いま、doPeriodicCleanup
メソッドの実行を、月末と毎週金曜日のp.m. 11:00として、タイマーにセットしたいとします。タイマーをセットするには、@Schedule
アノテーションを作成して、これを二つdoPeriodicCleanup
に付与します。一つ目は月末を指定し、二つ目は毎週金曜日のp.m. 11:00で、以下のコードのようになります。
@Schedule(dayOfMonth="last") @Schedule(dayOfWeek="Fri", hour="23") public void doPeriodicCleanup() { ... }
上記の例はメソッドにアノテーションを付与しています。標準アノテーションを使用するであろう場所ならどこでもアノテーションを複数付与可能です。たとえば、権限のないアクセス例外をハンドリングするクラスがあるとします。@Alert
アノテーションをマネージャーとその他の管理者としてクラスに付与可能です。
@Alert(role="Manager") @Alert(role="Administrator") public class UnauthorizedAccessException extends SecurityException { ... }
互換性の理由により、repeating annotationsは、Javaコンパイラにより自動的に生成されるコンテナアノテーション(container annotation)に格納されています。これを実現するためにコンパイラでは*1、二つの宣言がコードに要求されます。
Step 1: Declare a Repeatable Annotation Type
アノテーションの型は@Repeatable
メタアノテーションを付与する必要があります。カスタムSchedule
サンプルを例にとると、
public @interface Schedule { ... }
コードは以下のようになります。
@Repeatable(Schedules.class) public @interface Schedule { ... }
@Repeatable
メタアノテーションの括弧内の値は、Javaコンパイラがrepeating annotationを格納するために生成するコンテナアノテーションの型になります。この例では、コンテナアノテーションの型はSchedules
なので、@Schedule
repeating annotationが@Schedules
アノテーションに格納されます。
この宣言無しに同一アノテーションを付与することは、コンパイル時エラーを引き起こします。
Step 2: Declare the Containing Annotation Type
コンテナアノテーションは配列型のvalue
要素を持つ必要があります。配列要素の型はrepeatable annotationの型でなければなりません。Schedules
コンテナアノテーションの宣言は以下のようになります。
public @interface Schedules { Schedule[] value; }
Retrieving Annotations
リフレクションAPIで使用可能ないくつかのメソッドでアノテーションを検索できます。AnnotatedElement.getAnnotationByType(Class
Design Considerations
アノテーションを設計する際には、アノテーションのcardinalityを考慮しなければいけません。アノテーションはゼロ回か一回付与できますが、もしアノテーションに@Repeatable
がマークされた場合は複数回付与できます。また、@Target
メタアノテーションを使用することでアノテーションの使用箇所を制限できます。たとえば、自前のrepeatable annotationをメソッドとフィールドでのみ使用可能にできます。プログラマが使用するアノテーションが可能な限り柔軟性かつ強力であることを保障するために、自前のアノテーションを注意深く設計することが重要です。