【Java】 アノテーションを自分で定義してみる
はじめに
お世話になります、hosochinです
さて、今回は
アノテーションを自作
してみようと思います
具体的なサンプルコードの前にメタアノテーションについて軽く説明します
メタアノテーション
メタアノテーションとはアノテーションの宣言自体を修飾するアノテーションです
アノテーションを自分で定義する場合はこのメタアノテーションを使います
標準メタアノテーション
標準メタアノテーションについて以下に示します
アノテーションの種類 | 説明 |
Target | アノテーションの対象の要素を指定 |
Retention | アノテーションの存在期間を指定 |
Documented | アノテーションを文書化対象するjavadocへの指定 |
Inherited | アノテーションが自動的に継承されることを指定 |
Repeatable | 同じ対象要素に同じアノテーションを複数指定可能 |
Targetについて
アノテーションの対象要素を次のように指定します
@Target(ElementType.METHOD)
public @interface TutorialAnnotation { }
ElementTypeには以下の種類があります
ElementTypeの種類 | 説明 |
ANNOTATION_TYPE | アノテーション定義に使える |
CONSTRUCTOR | コンストラクタ定義に使える |
FIELD | フィールド定義に使える |
LOCAL_VARIABLE | ローカル変数定義に使える |
METHOD | メソッド定義に使える |
PACKAGE | パッケージ定義に使える |
PARAMETER | メソッドのパラメータ定義に使える |
TYPE | クラス、インタフェース(アノテーション、enum含む)定義に使える |
TYPE_PARAMETER | 型パラメータ定義に使える |
TYPE_USE | 型を使える場所全てに使える |
Retentionについて
アノテーションの存在期間を次のように指定します
@Retention(RetentionPolicy.RUNTIME)
public @interface TutorialAnnotation { }
RetentionPolicyには以下の種類があります
RetentionPolicyの種類 | 説明 |
SOURCE | アノテーションはコンパイラによって破棄される |
CLASS | アノテーションはコンパイラによってクラスファイルに記録される(デフォルトの設定) |
RUNTIME | アノテーションはコンパイラによってクラスファイルに記録され、実行時に参照可能 |
サンプルコード
- アノテーションを定義
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
String value();
}
- MyAnnotationアノテーションをクラスに付与
@MyAnnotation(value = "sample")
public class Sample {
}
- リフレクションを使って呼び出す
public class Main {
public static void main(String[] args) throws Exception {
// リフレクションでクラス名を探す
Class<?> targetClass = Class.forName("Sample");
// MyAnnotationアノテーションを探す
MyAnnotation myAnnotation = targetClass.getAnnotation(MyAnnotation.class);
System.out.println(myAnnotation.value());
}
}
- 実行結果
sample
アノテーションでコールバックパターン
自作アノテーションをちょっと応用してみて、コールバックパターンを書いてみます
今回は文字列をCamelケースからSnakeケースに、小文字から大文字に変換するプログラムを例にやってみようと思います
- アノテーションを定義
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyFilter{}
- MyFilterアノテーションをメソッドに付与
public class CamelToSnake {
// CamelケースからSnakeケースに変換
@MyFilter
public String replace(String str) {
return CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_UNDERSCORE, str);
}
}
public class Capitalize {
// 大文字に変換
@MyFilter
public String replace(String str) {
return str.toUpperCase();
}
}
- リフレクションを使って呼び出す
public class TutorialMyAnnotation {
private static class Filter {
Object object;
Method method;
Filter(Object object, Method method) {
this.object = object;
this.method = method;
}
}
private List<Filter> filters = new ArrayList<>();
public TutorialMyAnnotation(List<String> classNames) throws Exception {
for (String className : classNames) {
// リフレクションでクラス名を探す
Class clazz = Class.forName(className);
for (Method method : clazz.getMethods()) {
// MyFilterアノテーションを探す
if (method.isAnnotationPresent(MyFilter.class)) {
filters.add(new Filter(clazz.newInstance(), method));
}
}
}
}
public void print(String input) throws Exception {
String output = input;
for (Filter filter : filters) {
output = (String) filter.method.invoke(filter.object, output);
}
System.out.println(output);
}
}
public class Main {
public static void main(String[] args) throws Exception {
List<String> classNames = Arrays.asList(
"CamelToSnake", "Capitalize");
TutorialMyAnnotation tutorialMyAnnotation = new TutorialMyAnnotation(classNames);
tutorialMyAnnotation.print("replaceString");
}
- 実行結果
REPLACE_STRING
まとめ
アノテーションを自分で定義することができました
リフレクションについてもどこかでまとめたいなと思います
ディスカッション
コメント一覧
まだ、コメントがありません