Javaで重複したアノテーションを宣言する3つの方法

これまで気になる事がなかったので知らなかったのですが、Javaアノテーション(注釈)では同じアノテーションを同一のフィールド(クラス、メソッドなども同様)に宣言することができないようです。
例えばこんなコードはコンパイルエラーになります。

public class FooForm {
   @Regex(value = P1)
   @Regex(value = P2)
   public String someField;
}

複数の正規表現による妥当性チェックをアノテーションで行おうとしたわけですが、許可してくれません。ちょっと調べたところ、ちらほらと同じ事ではまっている人もいたので、まとめておきます。

解決策1ーアノテーションをネストさせて配列で保持する

解決方法の1つはネストさせる方法のようで、これがネットでは一番見られたケースです。

public @interface Regexes {
  Regex[] values;
}

こんなアノテーションを作り、配列として定義します。

public class FooForm {
   @Regexes(values = [@Regex(value = P1), @Regex(value = P2)]);
   public String someField;
}

ただ、問題としてアノテーションは継承ができないため、抽象クラスなどを定義し幾つかの種類をまとめて扱えるようにすることはできません。アノテーションの値にはObjectとすることもできないので、アノテーション毎に複数扱えるアノテーションを定義する必要があります。

解決策2ーアノテーションの設定値を配列にする

アノテーションを複数定義できないので複数作らない方法としては、複数の設定値を持たせる方法があります。つまり、こんな感じに定義するわけです。

public class FooForm {
   @Regex(values = [P1, P2]);
   public String someField;
}

これならばうまく2つのパターンを1つのアノテーションに定義できています。
しかし、問題もあり、次のように複数の設定を各アノテーションに付けたい場合です。

public class FooForm {
   @Regex(value = P1, msg = M1)
   @Regex(value = P2, msg = M1)
   public String someField;
}

エラーメッセージとチェックパターンでセットと考えてください。
これを解決策2で行おうとすると、次のようになります。

public class FooForm {
   @Regex(values = [P1, P2], msges = [M1, M2]);
   public String someField;
}

そもそも、2つのチェックが独立しているわけで、定義が密接になりすぎです。

解決策3ー別のアノテーションにする

最後の解決策としてアノテーションの型を新しく定義することです。つまり、次のようになります。

public class FooForm {
   @Regex(value = P1, msg = M1)
   @RegexMore(value = P2, msg = M1)
   public String someField;
}

これならば制限にはかかりませんが、必要な数だけアノテーションを定義する必要があり、処理する箇所も必要になります。とはいえ、高々5個もあれば十分などというケースであれば、アノテーションが宣言的に読める解決策3が妥当なのかもしれません。

というわけで、pirka-form では解決策3を採用して、複数の正規表現パターンに対応しようと思います。