やり直しJava

アノテーション

アノテーションは J2SE 5.0で追加されました。アノテーションはインタフェースの特別な形です

アノテーションは コンパイラやフレームワークに対して意図を伝えるためのメカニズムです。Java SEの標準ライブラリで用意されているアノテーションは 数も少なくそれほど強力な物はありません。しかし、Java EEやLombokライブラリなど アノテーションを有効に活用しているフレームワークに触れると、アノテーションの有用性を実感することができます。

この章では Java SEで用意されている 主なアノテーションを見ていき、続いて 独自アノテーションを定義する方法を簡単に見ていきます。

Java SEの主なアノテーション

Java SEの標準ライブラリにはいくつかのアノテーションが含まれています。その中でユーザが利用する主な物はjava.langパッケージに含まれる5種類のアノテーションです。ここではjava.langパッケージの5種類のアノテーションを紹介します。

@Override

スーパークラスのメソッドをオーバーライドしていることを明示します@Overrideアノテーションを付けておけば コンパイラがメソッドシグニチャのチェックを行い、シグニチャが違っている場合は コンパイルエラーにしてくれます。

オーバーライドで間違え易いのが Object.equals()メソッドで、SomeClassでequals()をオーバーライドする場合に、次のようにしてしまうと オーバーライドではなく オーバーロードになってしまいます。

class SomeClass {
    public boolean equals(SomeClass another) {
        return  XXX;
    }
}

これは、Object.equals()の引数がObjectであるため、SomeClassのequals()メソッドの引数もObjectにしないとオーバーライドにならないためです。このような場合に @Overrideアノテーションを付けておけば コンパイラによるチェックを受けることができ、コンパイルの時点で誤りを検出することができます。

テキストエディタでソースコードを編集していた時代は オーバーライドしたつもりがメソッド名や引数に間違いがあり オーバーライドではなく別のメソッドを定義してしまうようなミスが良く見受けられました。しかし、昨今では IDEでオーバーライドを選択するとスケルトンを生成してくれるため、このようなミスは防げるようになりました。

また、大抵のIDEは オーバーライドしているメソッドに@Overrideが付いていないと 警告やエラーとして検出する機能を持っています(IDEによってはデフォルトの設定を変更する必要があるかも知れません)。この機能を使えば、意図せずオーバーライドになってしまっているメソッドを検出することもできます。

まとめると、IDEと@Overrideアノテーションを利用することによって、オーバーライドしたつもりがオーバーライドになっていないメソッドと、オーバーライドしたつもりがないのにオーバーライドになってしまっているメソッドの両方を検出することができます。そのためには、プログラマがオーバーライドを意図している箇所では 常に@Overrideアノテーションを付けるべきです(IDEは オーバーライドを選択すると スケルトン生成と同時に@Overrideアノテーションも付けてくれます)。

尚、抽象クラスやインタフェースで定義されている抽象メソッドを実装する場合には @Overrideアノテーションを付ける必要はありませんが、つけても問題ありません。しかし、抽象クラスの具象メソッドとインタフェースのデフォルトメソッドのように実体を持ったメソッドをオーバーライドする場合には @Overrideアノテーションを付けるべきです。

@Deprecated

メソッドが非推奨であることを明示します。@Deprecatedが付けられたメソッドを呼び出すとコンパイラから警告が発せられます。

@SuppressWarning

コンパイラの警告を抑制します。コンパイラはたいてい有用な警告を発してくれますが、時には問題のない警告である場合もあります。そのような場合には @SuppressWarningを指定すると該当の警告を抑止することができます。ただし、闇雲に使ってしまうと 重要な警告を見逃すことになりかねないのため、問題がないことの確認を行った上で抑制するべきです

@SuppressWarningでは抑制する警告の種類を指定します。どういう値が設定できるかは コンパイラの実装によって異なりますが、「unchecked」(総称型で型引数を指定してない旨の警告を抑制)、「fallthrough」(case文においてbreak文が無いまま次のcaseに移る旨の警告を抑制)、「all」(全ての警告を抑制)などがあります。

@FunctionalInterface

インタフェースが関数型インタフェースであることを示します。インタフェースを関数型インタフェースとするには 一定の条件を満たす必要がありますが、関数型インタフェースの条件を満たしているかどうかをコンパイラにチェックさせることができます

関数型インタフェースについては「ラムダ式」の章の「関数型インタフェース」を参照してください。

@SafeVarargs

総称型の可変長引数をメソッド引数とする場合、潜在的にヒープ汚染を引き起こす可能性があるため、メソッド定義と そのメソッドを呼び出している箇所の両方に警告が表示されます。ただし、メソッド側で総称型の可変長引数を適切に扱うことにより、ヒープ汚染の可能性を排除することができます。その場合には @SafeVarargsアノテーションを指定して メソッドが安全であることを宣言することができます。

詳しくは「総称型」の章の「総称型配列」を参照してください。

独自アノテーションの定義

キーワード「@interface」で独自のアノテーションを定義することができます。ただ、アノテーション自体コンパイラやフレームワークに対して意図を伝えるのが主な用途のため、通常のアプリケーション開発において独自アノテーションの定義が必要なケースは滅多にないと思います。Javaによるフレームワークを開発するような場合には独自アノテーションを活用することができます

次は独自アノテーション定義の例です。

@Target(ElementType.FIELD, ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface SomAnnotation {
    String someValue();
}

この例では SomeAnnationというアノテーションを定義しています。SomeAnnotationはクラスのフィールドとメソッドに付与することができ、実行時にもアノテーションの情報が保持されます。また、someValueというStringの属性を持ちます。@Target、@Retention、@Inheritedはメタアノテーションと呼ばれます。各メタアノテーションの説明は後述します。

次はSomeAnnotationの使用例です。

@SomeAnnotation(someValue = "message")
public void someMethod() {
}

someMethod()にSomeAnnotationアノテーションをを付与し、someValue=”message”という属性を設定しています。

メタアノテーション

@Target、@Retention、@Inherited、@Repeatable は独自アノテーションの情報を定義するためのメタアノテーションです。

主なメタアノテーション
メタアノテーション内容
@Targetアノテーションを付与できる対象を指定します。ElementType列挙型で定義されている値を指定します。ElementType列挙型では次のような列挙子が定義されています。

  • TYPE(クラス、インタフェース定義。enumや アノテーション定義も含む)
  • FIELD(フィールド定義)
  • METHOD(メソッド定義)
  • PARAMETER(メソッド引数)
  • CONSTRUCTOR(コンストラクタ定義)
  • LOCAL_VARIABLE(ローカル変数)
  • ANNOTATION_TYPE(アノテーション定義)
  • PACKAGE(パッケージ)
@Retentionアノテーションの有効範囲を指定します。RetentionPolicy列挙型で定義されている値を指定します。RetentionPolicy列挙型では次の列挙子が定義されています。

  • SOURCE(ソース内のみ。コンパイルすると classファイルに残らない。)
  • CLASS(デフォルト値。classファイル内に保持されるが、実行時には読み込まれない。)
  • RUNTIME(classファイル内に保持され、実行時にも読み込まれる。)
@Inheritedアノテーション情報がサブクラスにも継承されるかどうかを指定します
@Repeatableアノテーションを繰り返し指定できる場合に指定します。値にはコンテナアノテーション型のクラスを指定します。繰り返し指定可能なアノテーションはコンテナアノテーション型のクラスオブジェクトとして取り出すことができます。

アノテーションの属性

@SuppressWarningのように、アノテーションには属性(名前=値)を受け取る物があります。前述の独自アノテーションSomeAnnotationの例で言うと someValueが属性になります。属性として指定できる型には制限があり、プリミティブ型、String、Class、列挙型、アノテーションおよびそれらの一次配列が指定可能です。属性が一つだけで、かつ属性の名前が「value」の場合に限り、「属性名=」の部分を省略することができます。(@SuppressWarning、@Target、@Retentionはそれに該当するため「value=」の部分が省略可能です。)

尚、属性を持たないアノテーションは 要素に印を付けるだけなのでマーカーアノテーションと呼ばれます。

独自アノテーションの処理

独自アノテーションが指定されても それを解釈して適切な処理を行わないと意味がありません。独自アノテーションを処理するには リフレクションでアノテーション情報を取り出すことになりますが、実行時に取り出せるアノテーションは@Retention(RetentionPolicy.RUNTIME) が指定されているアノテーションのみになります。