【问题标题】:Preventing Java annotation to be added to final field防止将 Java 注释添加到最终字段
【发布时间】:2019-10-31 08:13:35
【问题描述】:
@MyAnnotation
final Integer value;

是否可以配置自定义注释 MyAnnotation 以导致上述编译时错误,但添加到非最终字段时不会出现编译时错误? (与访问级别无关)。

背景信息:在我们的代码中,我们有一个特殊的注解,它通过来自外部类的反射触发修改,而这不能对 final 字段进行,所以我们想在编译时警告用户注解(目前我们运行时检查会引发异常)。

【问题讨论】:

  • 您可以创建一个annotation processor,如果@MyAnnotation 在最后一个字段上,则会发出错误。但是,您可能无法强制使用注释处理器。

标签: java annotations


【解决方案1】:

一个选项是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 确实允许使用注释的类(例如 Foo.class)。使用类 API 可以更易于使用、更易于推理和更易于阅读 - 如果您的注解类对您的处理器类可见,则使用它。


然后您需要告诉javac 使用注释处理器。请参阅tool specification 了解更多信息;具体来说,请查看--processor-path--processor-module-path-processor-proc 标准选项以及the section 注释处理。

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-10-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-09-15
    • 1970-01-01
    • 2013-03-07
    相关资源
    最近更新 更多