2009-09-07 15 views
41

注釈値に定数を使用したいと思います。それはこの場所で配列イニシャライザなければならないよう注釈に配列定数を使用する方法

interface Client { 

    @Retention(RUNTIME) 
    @Target(METHOD) 
    @interface SomeAnnotation { String[] values(); } 

    interface Info { 
     String A = "a"; 
     String B = "b"; 
     String[] AB = new String[] { A, B }; 
    } 

    @SomeAnnotation(values = { Info.A, Info.B }) 
    void works(); 

    @SomeAnnotation(values = Info.AB) 
    void doesNotWork(); 
} 

定数Info.AInfo.BアレイInfo.AB注釈ではなく、使用することができます。アノテーション値は、クラスのバイトコードにインライン化できる値に制限されています。 Infoがロードされたときに構成されなければならないので、これはアレイ定数に対しては不可能です。この問題の回避策はありますか?

+1

Eclipseのエラーをコンパイルきわめて明確である:「注釈属性Client.doesNotWork.valuesの値が配列初期化子でなければなりません」。それは非常に明確ですが、私は回避策はないと思います。 – skaffman

答えて

42

いいえ、回避策はありません。

+4

しかし、なぜですか?なぜ??? –

+6

@ JensSchauderアノテーションはコンパイル時に処理されるため、コードが実行される前でも処理されます。配列 'AB'はまだ存在しません。 –

13

注釈値を実際のデータ値のキーとなる列挙型にしないでください。

enum InfoKeys 
{ 
A("a"), 
B("b"), 
AB(new String[] { "a", "b" }), 

InfoKeys(Object data) { this.data = data; } 
private Object data; 
} 

@SomeAnnotation (values = InfoKeys.AB) 

これは型の安全性を向上させることができますが、あなたはそのアイデアを得るでしょう。

+3

+1いい考え。コンパイルされた例はさらに良いでしょう;-) – skaffman

+1

良いアイデア。アノテーションを変更できる場合はこれで問題はありません。あなたは@interface SomeAnnotation {InfoKeys values();を使う必要があります。 }。悲しいことに、アノテーションのタイプ自体を変更することはできません。 –

+0

注釈の種類を変更すると、この列挙の値への使用が制限されます。ほとんどのユースケースでは、これは制限的です。 –

1

注釈パラメータ値として直接を配列を渡す方法はありませんが、が効果的に、これはすべてのために動作しない場合があり、あなたがあなたのアノテーションを使用する予定の方法に応じて、(同様の動作を取得する方法あり使用事例)。

ここでは、クラスInternetServerがあり、hostnameというプロパティがあるとします。通常のJava Validationを使用して、オブジェクトに「予約済みの」ホスト名がないようにしたいと考えています。ホスト名の検証を扱う注釈に、予約されたホスト名の配列を(やや精巧に)渡すことができます。

注意:Java検証では、この種のデータを渡すために「ペイロード」を使用するのがより一般的です。私はこの例を少し一般的なものにしたいので、カスタムインターフェイスクラスを使用しました。

// InternetServer.java -- an example class that passes an array as an annotation value 
import lombok.Getter; 
import lombok.Setter; 
import javax.validation.constraints.Pattern; 

public class InternetServer { 

    // These are reserved names, we don't want anyone naming their InternetServer one of these 
    private static final String[] RESERVED_NAMES = { 
     "www", "wwws", "http", "https", 
    }; 

    public class ReservedHostnames implements ReservedWords { 
     // We return a constant here but could do a DB lookup, some calculation, or whatever 
     // and decide what to return at run-time when the annotation is processed. 
     // Beware: if this method bombs, you're going to get nasty exceptions that will 
     // kill any threads that try to load any code with annotations that reference this. 
     @Override public String[] getReservedWords() { return RESERVED_NAMES; } 
    } 

    @Pattern(regexp = "[A-Za-z0-9]{3,}", message = "error.hostname.invalid") 
    @NotReservedWord(reserved=ReservedHostnames.class, message="error.hostname.reserved") 
    @Getter @Setter private String hostname; 
} 

// NotReservedWord.java -- the annotation class 
import javax.validation.Constraint; 
import javax.validation.Payload; 
import java.lang.annotation.Documented; 
import java.lang.annotation.Retention; 
import java.lang.annotation.Target; 

import static java.lang.annotation.ElementType.ANNOTATION_TYPE; 
import static java.lang.annotation.ElementType.FIELD; 
import static java.lang.annotation.RetentionPolicy.RUNTIME; 

@Target({FIELD, ANNOTATION_TYPE}) 
@Retention(RUNTIME) 
@Constraint(validatedBy=ReservedWordValidator.class) 
@Documented 
public @interface NotReservedWord { 

    Class<? extends ReservedWords> reserved(); 

    Class<?>[] groups() default {}; 

    Class<? extends Payload>[] payload() default {}; 

    String message() default "{err.reservedWord}"; 

} 

// ReservedWords.java -- the interface referenced in the annotation class 
public interface ReservedWords { 
    public String[] getReservedWords(); 
} 

// ReservedWordValidator.java -- implements the validation logic 
import javax.validation.ConstraintValidator; 
import javax.validation.ConstraintValidatorContext; 
import java.util.Map; 
import java.util.concurrent.ConcurrentHashMap; 

public class ReservedWordValidator implements ConstraintValidator<NotReservedWord, Object> { 

    private Class<? extends ReservedWords> reserved; 

    @Override 
    public void initialize(NotReservedWord constraintAnnotation) { 
     reserved = constraintAnnotation.reserved(); 
    } 

    @Override 
    public boolean isValid(Object value, ConstraintValidatorContext context) { 
     if (value == null) return true; 
     final String[] words = getReservedWords(); 
     for (String word : words) { 
      if (value.equals(word)) return false; 
     } 
     return true; 
    } 

    private Map<Class, String[]> cache = new ConcurrentHashMap<>(); 

    private String[] getReservedWords() { 
     String[] words = cache.get(reserved); 
     if (words == null) { 
      try { 
       words = reserved.newInstance().getReservedWords(); 
      } catch (Exception e) { 
       throw new IllegalStateException("Error instantiating ReservedWords class ("+reserved.getName()+"): "+e, e); 
      } 
      cache.put(reserved, words); 
     } 
     return words; 
    } 
} 
0
import java.lang.annotation.Documented; 
import java.lang.annotation.ElementType; 
import java.lang.annotation.Retention; 
import java.lang.annotation.RetentionPolicy; 
import java.lang.annotation.Target; 

@Target(ElementType.METHOD) 
@Retention(RetentionPolicy.RUNTIME) 
@Documented 
public @interface Handler { 

    enum MessageType { MESSAGE, OBJECT }; 

    String value() default ""; 

    MessageType type() default MessageType.MESSAGE; 

} 
+0

コードに説明を追加できますか? – Peanut

+0

これは正しくても質の低い回答です。説明を追加すると、それが大幅に改善されます(@Peanutによると) – mccainz

関連する問題