Meta-Annotations について
このエントリは、アイディアや情報の提供を呼びかけるためのものです。
結局何がしたいのか
自前の AnnotationProcessor で Meta-Annotation 的な機能をサポートしたい。
で、そもそも Meta-Annotation ってこんなんで良いんだっけ?というエントリです。
解決したい問題
同じアノテーションの組合せを沢山のクラスに設定したくない
アノテーションの合成
こういう二つのアノテーションがあったとして
public @interface Transactional {}
public @interface Service {}
これらを一つにまとめるアノテーションを定義したい。
@Service
@Transactional
public @interface TransactionalService {}
で、これを使うコードと使ってないコードが同じであるとみなしたい。
@TransactionalService
public class MyService {}
@Service
@Transactional
public class MyService {}
値の設定を伴う合成
値を設定するケースでも同じように書きたい。以下のようなアノテーションを定義する。
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
public @interface RuntimeType {}
こういう風に使いたい。
@RuntimeType
public @interface MyTypeAnnotation {}
@RuntimeType
を使った宣言は、以下の宣言と同じであるとみなしたい。
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
public @interface MyTypeAnnotation {}
ここでの重要なポイントは、@Target
や@Retention
ではデフォルト値が存在しないが、@RuntimeType
宣言時に必要な値を埋め込んでいることである。
汎用的な定義を具体的な定義として読み替える
以下のような使い方も想定できる。
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.TYPE;
import java.lang.annotation.Target;
@Target({ TYPE, METHOD })
public @interface Transactional {
TxType value();
enum TxType {
REQUIRED, REQUIRES_NEW, MANDATORY, SUPPORTS, NOT_SUPPORTED, NEVER;
}
}
@Transactional
はデフォルト値の無い汎用的な宣言だが、値の設定を伴わない具体的な宣言に定義しなおしたい。
import static tx.Transactional.TxType.REQUIRED;
@Transactional(REQUIRED)
public @interface TxRequired {}
つまり、MyTxService
とMySimpleTxService
は同じように解釈されてほしい。
import static tx.Transactional.TxType.REQUIRED;
@Transactional(REQUIRED)
public class MyTxService {}
@TxRequired
public class MySimpleTxService {}
値の上書きを伴う合成
public @interface Range {
long min() default Long.MIN_VALUE;
long max() default Long.MAX_VALUE;
}
@Range
に対して@Positive
という値を絞り込んだアノテーションを作る。
@Range(min = 0L)
public @interface Positive {
long max();
}
@Positive
は、max を上書きできるが、min は上書きできない。
@Range
では、max を設定しない場合、Long.MAX_VALUE
がデフォルト値として適用されるが、@Positive
では max を設定しなければならない。
以下の例では、val と pos は同じ意味をもつが、デフォルト値のある@Range
とデフォルト値の無い@Positive
ではコンパイルエラーの出力が異なる。
public class MyClass {
@Range(min = 0, max = 200)
long val;
@Positive(max = 200)
long pos;
}