防止将 Java 注释添加到最终字段

Preventing Java annotation to be added to final field

@MyAnnotation
final Integer value;

是否可以配置一个自定义注解MyAnnotation,这样会导致上面的编译时错误,但添加到非final字段时不会出现编译时错误? (不考虑访问级别)。

背景信息:在我们的代码中,我们有一个特殊的注释,可以通过外部反射触发修改class,这不能用于最终字段,所以我们想警告用户注释在编译时(目前我们有一个会抛出异常的运行时检查)。

一个选项是 annotation processor。例如,如果您有以下注释:

package com.example;

import java.lang.annotation.ElementType;
import java.lang.annotation.RetentionPolicy;

@Retention(RetentionPolicy.RUNTIME) // must be at least SOURCE
@Target(ElementType.FIELD)
public @interface Foo {}

如果 @Foo 出现在 final 字段上,则以下命令将发出错误:

package com.example;

import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.annotation.processing.SupportedSourceVersion;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.Element;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.TypeElement;
import javax.tools.Diagnostic.Kind;
import java.util.Set;

@SupportedAnnotationTypes("com.example.Foo")
@SupportedSourceVersion(SourceVersion.RELEASE_13) // or whatever version you support
public class FooProcessor extends AbstractProcessor {

  @Override
  public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
    if (!annotations.isEmpty()) {
      // This processor is only declared to process one annotation
      TypeElement foo = annotations.iterator().next();

      for (Element element : roundEnv.getElementsAnnotatedWith(foo)) {
        for (AnnotationMirror mirror : element.getAnnotationMirrors()) {
          if (mirror.getAnnotationType().asElement().equals(foo)
              && element.getModifiers().contains(Modifier.FINAL)) {
            // processingEnv is a protected field of AbstractProcessor
            processingEnv.getMessager()
                .printMessage(Kind.ERROR, "Field cannot be final.", element, mirror);
            break;
          }
        }
      }
    }
    return true;
  }
}

在上面的代码中,我只使用模型 API(例如 TypeElement)来引用 Foo 注释。但是请注意,如果注释可见(即您的处理器依赖于注释),注释处理器 API 确实允许使用注释的 class(例如 Foo.class) ).使用 class API 可以更容易使用,更容易推理,也更容易阅读——如果你的注释 class 对你的处理器 class 可见,请使用它。 =24=]


然后您需要告诉 javac 使用注释处理器。请参阅有关注释处理的 tool specification for more information; specifically, look at the --processor-path, --processor-module-path, -processor, and -proc standard options as well as the section

请注意,您可能无法强制使用注释处理器。