是否可以使用注释处理器创建静态嵌套类?

时间:2017-11-15 09:38:13

标签: java annotations annotation-processing

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

我创建了@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关键字(因为它被完全视为最高级别的类)。

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

1 个答案:

答案 0 :(得分:1)

为嵌套类创建文件似乎不是正确的方法 - 如果生成的源文件已编译,它将被视为顶级类,即使名称< em>看起来像<嵌套类的。

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

  

此外,每个嵌套类和嵌套接口的 constant_pool 表必须引用其封闭类,因此,每个嵌套类和嵌套接口都将为每个封闭类和每个嵌套类提供InnerClasses信息。它自己的嵌套类和接口。

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

实际上,您不应该使用注释处理器更改现有类。无论如何,似乎有可能,如here所述。

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

相关问题