如何在注释中使用数组常量
我想使用常量的注释值。
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.A
和Info.B
可以在注释中使用,但不能在Info.AB
数组中Info.AB
因为它必须是这个地方的数组初始值设定项。 注释值被限制为可以内联到类的字节码中的值。 这是不可能的数组常量,因为它必须构build时加载Info
。 有没有解决这个问题的方法?
不,没有解决方法。
为什么不将注释值设置为一个枚举值,它们是您想要的实际数据值的关键?
例如
enum InfoKeys { A("a"), B("b"), AB(new String[] { "a", "b" }), InfoKeys(Object data) { this.data = data; } private Object data; } @SomeAnnotation (values = InfoKeys.AB)
对于types安全,这可以得到改进,但是你明白了。
虽然没有办法直接将数组作为注释参数值传递,但有一种方法可以有效地获得类似的行为(取决于您计划如何使用注释,这可能不适用于每个用例)。
这里有一个例子 – 假设我们有一个InternetServer
类,它有一个hostname
属性。 我们希望使用常规的Javavalidation来确保没有对象具有“保留的”主机名。 我们可以(稍微精心地)将一个保留的主机名数组传递给处理主机名validation的注释。
告诫 – 使用Javavalidation,使用“有效载荷”来传递这种数据更为习惯。 我想这个例子是更通用的,所以我使用了一个自定义的接口类。
// 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; } }
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; }