はじめに
SpringやDomaを使っているとよく出てくるアノテーションですが、正確な役割などは
あまり理解せず、フィーリングで使ってきていたので基礎から立ち返りたいと思います。
アノテーションとは?
@Override
のように「@
+ 名前」のような形式で表現されるものを
アノテーション
と呼びます。
これらをメソッドやクラスなどjavaのあらゆる機能に付けることで利用します。
アノテーションには以下のような役割があります。
クラスやメソッドなどに対して補足的な情報を付け加えるためのもの。
このように、あくまで補足的な情報を付け加えるためのものなので、アノテーションそれ自体に
なにか機能が含まれているわけではありません。
いわば、ソースコードのコメントのようなものです。
したがって、それだけでは処理的には何の意味もありません。
アノテーションが必要な理由
それならば、どうしてアノテーションを付ける必要があるのでしょうか?
それはアノテーションと特定の機能を組み合わせることで、強力な機能を実現することができるからです。
- コンパイラなどと組み合わさることで、通常では検出できないようなエラーやバグを探知する。また、逆にコンパイルによる特定の警告を抑制することもできる。(例:
@Override
、@SuppressWarnings
) - アノテーションを元にして、コンパイル時に新しいクラスを生成できる。
- リフレクションAPI(実行時の型情報を取得する機能)を用いることで、特定のアノテーションを対象にして処理を行うことができる。
アノテーションの利用方法
アノテーションの呼び出し方
基本的には以下のような形式で呼び出すことができます。
@SomeName
あるいは
@SomeName(パラメーター)
このような形式で利用対象に情報を追加できます。
例えば、以下のアノテーションではvalueでリクエスト先のURLを定義しています。
// ~/sampleからリクエストが送信された場合に以下のメソッドを実行する。
@RequestMapping(value = "/sample")
public String method() {
return "index";
}
なお、パラメーターを指定しなかった場合はアノテーションクラスで定義されているデフォルト値が
自動で設定されます。
また、アノテーションクラスでパラメーターに対してデフォルト値が定義されていなかった場合は、利用側でのパラメーター設定が必須となります。
具体例
@SuppressWarnings
コンパイラによる警告を抑制するために使用されるアノテーションです。
基本的には警告が出ないように書くのがベストなのですが、特定のケースにおいては警告が
不適切な場合があるので、そのような場合に利用します。
パラメーターに抑制したい警告の名称を与えることによって、対象の警告を抑制することができます。
// raw型は使用する型が限定されていないため、型変換時にコンパイラが型チェックを行えず、警告が出力される。
void foo(List inputList) {
// 対象となる処理にアノテーションをつけることで、警告が抑制される。
@SuppressWarnings("unchecked")
List<String> list = (List<String>) inputList;
}
補足:@SuppressWarnings
のパラメーター一覧は以下のコマンドを実行することで出力される。
javac -X
@Override
基本的には親クラスのメソッドをオーバーライドする際に利用するアノテーションですが、
実際にはこのアノテーションがなくてもオーバーライドすることはできます。
しかし、このアノテーションをつけることでオーバーライドした際に親クラスのメソッドと定義が異なっていた場合にコンパイラがコンパイルエラーとして出力してくれるので間違えることなく、オーバーライドを進めることができます。
public class Animal {
protected void eat(int num, String food) {
System.out.println("Animal eats" + num + food);
}
}
public class Dog extends Animal {
@Override // NG(引数の数が足りないため、コンパイルエラーとなる。)
protected void eat(int num) {
System.out.println("Dog bites" + num);
}
@Override // NG(引数の型が一致しないため、コンパイルエラーとなる。)
protected void eat(int num, int food) {
System.out.println("Dog bites" + num + food);
}
@Override // OK
protected void eat(int num, String food) {
super.eat(num, food);
System.out.println("Dog bites" + num + food);
}
}
アノテーション作成方法
アノテーションを自作する際には以下の点に注意してください。
- すべてのアノテーションクラスはjava.lang.Annotationクラスを明示的に継承する。
-
@Target
,@Retention
などのメタアノテーションに関しては、明記されてない場合、デフォルト値が設定される。(メタアノテーションに関しては後述) - パラメーターはインターフェイスのメソッドと同じように定義する。
- パラメーターには
default
キーワードを用いて、デフォルト値を設定できる。 - デフォルト値を設定しない場合、必須パラメーターとなる。
// アクセス修飾子 @interface インターフェース名で定義する。
@Target(ElementType.METHOD)
@Retention(RetentinoPolicy.SOURCE)
public @interface hoge (extends Annotation) {
int num();
String value() default "";
}
アノテーションの種類
メタアノテーションについて
アノテーションに付けて利用するアノテーションをメタアノテーション
と呼びます。
メタアノテーションとして代表的なものとしては、以下のようなものがあります。
これらは基本的にどんなアノテーションにも付いており、省略された場合は
デフォルト値が設定されるようになっています。
@Retention
retentionは保持という意味を持つように、アノテーションの有効期限を設定することができます。
有効期限は、RetentionPolicy型のenum値として以下の3つが定義されています。
- SOURCE:ソースコードでのみ有効であり、コンパイル時には削除される。(例:@Override、@SuppressWarnings:コンパイラやIDEに対して、エラーを表示させるようにしたり、警告を抑制させたりする。)
- CLASS:コンパイラにより生成されたクラスファイルに記録される。JVMによる実行時には削除されるので、リフレクションを用いても情報を取得することはできない。(例:@AutoValue→開発者が作成したコードを分析して、新しいクラスを生成する。)
- RUNTIME:クラスファイルにも記録され、かつ実行時にJVMにも認識してもらう。基本的にリフレクション目的で使用する場合に設定する必要がある。(例:@Deprecated)
なお、@Retentionを省略した場合のデフォルト値はRetentionPolicy.CLASS
となります。
@Target
アノテーションが適用可能なコンテキストを示すメタアノテーションです。
例えば、@Target(ElementType.METHOD)
の場合、対象のアノテーションはメソッドのみに適用可能です。
なお、コンテキストはenum型のElementTypeクラスで定義されており、以下のような値を持ちます。
- METHOD:メソッド
- FIELD:フィールド
- PARAMETER:関数のパラメーター
- PACKAGE:パッケージ
- CONSTRUCTOR:コンストラクタ
- TYPE:変数の型
- etc.
また、コンテキストは複数指定することも可能です。
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER})
なお、@Targetを省略した場合、タイプパラメーターを除くすべてのコンテキストで適用可能なアノテーションと認識されます。
参考記事
unchecked warningの意味
アノテーション作成方法
[ちょっと特殊なJavaのアノテーション]
(https://qiita.com/kashira2339/items/6450714e42c37b441514)