Top / Java / Tiger / Annotation

アノテーションの概要

アノテーションとはTigerから導入された、クラスやメソッドに注釈(Annotation)を付与することができる機構です。通常のコメント等とは異なり、アノテーションの記述はプログラムで解析したり、その内容を見てプログラムがなにか処理を実行したりすることができます。

Tigerに標準で入ってるのはたとえば Override とか Deprecated ですね。たとえばオーバライドされてるメソッドに

@Override
public void hoge(){
}

などと書くことで、スーパクラスのメソッドをオーバライドしたよということを明示することができます。「オーバライドしたよ」ということを注釈してるわけですね。

Eclipseなどはたとえば、下記のようにタイプミスをした場合、

@Override
public void hhoge(){ <-hogeをhhogeとしちゃった
}

スーパクラスにはこのメソッドhhogeがないので、それを検知してエラーを通知してくれます。

アノテーションの作り方

さて自分でアノテーションを定義する方法についてですが、まず先のTigerのOverrideアノテーションを見てみます。アノテーションはふつうのインタフェース定義のようですが、下記のように@をつけて定義をするのが特徴です。

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}

Overrideアノテーションの定義自体にも、アノテーションが記述されてるのが分かります。先の注釈の考え方でいくと

@Target(ElementType.METHOD)

は「このOverride注釈を書くことができるのは、メソッドに対してだけです」という注釈だし

@Retention(RetentionPolicy.SOURCE)

は「このOverride注釈はソースコード上だけで、クラスファイルには記録されない」という注釈になります。

実際に作ってみる

実際に作ってみましょう。たとえば、ビジネスロジック(以下、サービスと呼びます)がたくさんあって、それを生成するファクトリがあったとします。そのファクトリは

public class ServiceFactory implements IServiceFactory {
    public <T> T createService(Class<T> type) {
      なんか、生成処理。
    }
}

とインタフェースの型を渡すと、その型の実装クラスを返す、なんてメソッドが定義されてたとします。

このファクトリに対して、インタフェース名、実装クラス名などの注釈をつけてみます。たとえばこんな感じです。

@Services( {
        @Service(type = IMessageService.class, value = MessageService.class),
        @Service(type = ILogger.class, value = ApplicationLogger.class, singleton = true) })
public class ServiceFactory implements IServiceFactory {
     public <T> T createService(Class<T> type) {
        Annotationをみて、なんか処理。
    }
}
  • Serviceアノテーションは、インタフェース名(type)と実装クラス名(value)の組を記述しています。
  • Servicesアノテーションは、@Serviceアノテーションの配列を保持しています。

こんな感じにファクトリにアノテーションで、設定を記述することができるわけですね。

これらのアノテーションの定義方法

この2つのアノテーションは以下のように定義しています。

  • Service
    @Inherited
    @Target(ElementType.TYPE) // クラスにのみ
    @Retention(RetentionPolicy.RUNTIME) // プログラムからアクセスしたい。
    @Documented
    public @interface Service {
        Class<?> type();
        Class<?> value();
        boolean singleton() default false;
    }
  • Services
    @Inherited
    @Target(ElementType.TYPE) // クラスにのみ
    @Retention(RetentionPolicy.RUNTIME) // プログラムからアクセスしたい。
    @Documented
    public @interface Services {
        Service[] value();
    }

Serviceアノテーションは、

@Service(type = IMessageService.class, value = MessageService.class)

このように設定値type,valueを持ちますが、これらはアノテーションの定義の

Class<?> type();
Class<?> value();

に対応しています。戻り値が、記述可能な型を定義しています。

また

@Service(type = ILogger.class, value = ApplicationLogger.class, singleton = true)

のsingletonという注釈は、先の記述のように記述を省略することもできます。この注釈は

boolean singleton() default false;

に対応しています。型はboolean で、設定しないとデフォルト値でそれはfalseですということですね。

次にServicesですが、

@Services({@Service(... ), @Service(... )})

のように配列のような記述をしています。これは

public @interface Services {
    Service[] value();
}

に対応しています。さらにvalueという変数は特殊で、一つの場合は省略可能という変数です。省略せずに書くと、

@Services(value = {@Service(... ), @Service(... )})

となるわけですが、valueだけなら冗長なので書かなくてもいいよって事になっています。それぞれのアノテーションに対しても

@Target(ElementType.TYPE) // クラスのみ

などと注釈がされてますが、これもvalueの省略形の記述というになりますね。

アノテーションに記載してあるアノテーションの意味。

まとめ中。

プログラムからアクセスしてみる。

さて先述のようにファクトリに対してアノテーションを記述しましたが、実際にプログラムからそれらの注釈にアクセスしてみます。といっても通常の変数にアクセスするのとあまり変わりません。以下のように処理をおこなう事が可能です。

public <T> T createService(Class<T> type) {
  // アノテーションを取得する。
  Services servicess = this.getClass().getAnnotation(Services.class);
  if (servicess == null) {
    return null;
  }
  // ServicesからServiceの配列を取得。さっきのメソッド名でアクセスできる
  Service[] services = servicess.value();
  for (Service service : services) {
    Class<?> typee = service.type();
    Class<?> clazz = service.value();
    if (service.singleton()) {
      // キャッシュするとか
    }
  }
  return null;
}

このようにプログラム内からアノテーションの記述にアクセスすることができます。


この記事は

選択肢 投票
おもしろかった 10  
そうでもない 1  

Top / Java / Tiger / Annotation

現在のアクセス:8951


トップ   編集 凍結 差分 バックアップ 添付 複製 名前変更 リロード   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS
Last-modified: 2022-05-12 (木) 14:50:15 (712d)