【问题标题】:Is it possible to create static nested class using annotation Processor?是否可以使用注释处理器创建静态嵌套类?
【发布时间】:2017-11-15 09:38:13
【问题描述】:

我想使用注解处理器创建一个静态嵌套类。有可能吗?

我创建了@MyAnnotation注解:

package annotationprocessing;

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;

@Documented
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
}

和注释处理器:

package annotationprocessing;

import java.io.BufferedWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.annotation.processing.Filer;
import javax.annotation.processing.ProcessingEnvironment;
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.TypeElement;
import javax.lang.model.element.VariableElement;
import javax.lang.model.util.Elements;
import javax.tools.JavaFileObject;

@SupportedAnnotationTypes({"annotationprocessing.MyAnnotation"})
@SupportedSourceVersion(SourceVersion.RELEASE_8)
public class MyAnnotationProcessor extends javax.annotation.processing.AbstractProcessor {

    private Filer filerUtils;
    private Elements elementUtils;
    private TypeElement myAnnotationTypeElement;
    private Map<TypeElement, List<VariableElement>> annotatedFields;

    @Override
    public synchronized void init(ProcessingEnvironment processingEnv) {
        super.init(processingEnv);

        filerUtils = processingEnv.getFiler();
        elementUtils = processingEnv.getElementUtils();
        myAnnotationTypeElement = elementUtils.getTypeElement(MyAnnotation.class.getCanonicalName());
    }

    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        annotatedFields = new HashMap<>();

        roundEnv.getElementsAnnotatedWith(myAnnotationTypeElement)
                .stream()
                .map(element -> (VariableElement) element)
                .forEach(this::processAnnotation);

        if (annotatedFields.isEmpty()) {
            return true;
        }

        System.err.println(annotatedFields);

        for (Map.Entry<TypeElement, List<VariableElement>> entry : annotatedFields.entrySet()) {
            TypeElement enclosingClass = entry.getKey();

            try {
                JavaFileObject javaFileObject = filerUtils.createSourceFile(enclosingClass.getQualifiedName().toString() + "$Nested");
                try (BufferedWriter writer = new BufferedWriter(javaFileObject.openWriter())) {
                    if (elementUtils.getPackageOf(enclosingClass).getQualifiedName().length() > 0) {
                        writer.write("package " + elementUtils.getPackageOf(enclosingClass).getQualifiedName() + ";");
                        writer.newLine();
                    }
                    writer.write("public /*static*/ class " + enclosingClass.getSimpleName() + "$Nested {");
                    writer.newLine();
                    for (VariableElement varElement : entry.getValue()) {
                        writer.write("static int " + varElement.getSimpleName() + ";");
                        writer.newLine();
                    }
                    writer.newLine();
                    writer.write("}");
                }
            } catch (IOException ex) {
                Logger.getLogger(MyAnnotationProcessor.class.getName()).log(Level.SEVERE, null, ex);
            }
        }

        return true;
    }

    private void processAnnotation(VariableElement sharedElement) {
        TypeElement enclosingClass = (TypeElement) sharedElement.getEnclosingElement();
        annotatedFields.putIfAbsent(enclosingClass, new ArrayList<>());
        annotatedFields.get(enclosingClass).add(sharedElement);
    }
}

编译类时(javac -processor annotationprocessing.MyAnnotationProcessor -cp annotationprocessing.jar TestClass.java

package org.full.path;
import annotationprocessing.MyAnnotation;
public class TestClass {

    public static class OtherNested {
        static int staticInt;
    }

    @MyAnnotation
    int staticInt;
    public static void main(String[] args) {
        System.out.println("other: " + TestClass.OtherNested.staticInt);
        //System.out.println("not working: " + TestClass.Nested.staticInt);
        System.out.println("generated: "+TestClass$Nested.staticInt);
    }
}

MyAnnotationProcessor 处理源文件并生成TestClass$Nested.class 文件,但它只能通过使用TestClass$Nested 名称而不是像嵌套类中的TestClass.Nested 来访问。此外,我不能在生成代码中使用static 关键字(因为它被完全视为最高级别的类)。

也许有办法通过添加静态嵌套类来完全重写输入源代码?

【问题讨论】:

  • 我知道这很旧,但如果您使用以下修复程序,您可以编写一个静态嵌套类:filerUtils.createSourceFile(elementUtils.getBinaryName(enclosingClass) + "$Nested")

标签: java annotations annotation-processing


【解决方案1】:

为嵌套类创建一个 source 文件在这里似乎不是正确的方法 - 如果生成的源文件被编译,它将被视为顶级类,即使名称 看起来类似于嵌套类。

嵌套类和封闭类在其字节码中相互引用。来自JVM Specification, Chapter 4.7.6. The InnerClasses Attribute,关于classes[]项:

此外,每个嵌套类和嵌套接口的 constant_pool 表必须引用其封闭类,所以总的来说,每个嵌套类和嵌套接口都将具有每个封闭类和每个封闭类的 InnerClasses 信息它自己的嵌套类和接口。

因此,您可以为嵌套类创建一个 .class 文件(内部类具有正确的字节码结构),然后使用一些字节码操作库来更改封闭类的实现。

您实际上不应该使用注释处理器更改现有类。无论如何,这似乎是可能的,正如here所解释的那样。

所有这一切看起来都需要付出很多努力。如果可能的话,我会避免这一切并尝试不同的方法。很难说出您的用例是什么,但可能会生成TestClass 的子类并将您的静态类放入子类或类似的东西中。

【讨论】:

  • 嵌套类(静态内部类)没有对外部类的引用。
  • @famir 你甚至可以在静态嵌套类上调用Nested.class.getEnclosingClass()
猜你喜欢
  • 2014-08-30
  • 1970-01-01
  • 1970-01-01
  • 2018-12-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多