我想使用注释处理器创建一个静态嵌套类。有可能吗?
我创建了@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
关键字(因为它被完全视为最高级别的类)。
也许有一种方法可以通过添加静态嵌套类来完全重写输入源代码?
答案 0 :(得分:1)
为嵌套类创建源文件似乎不是正确的方法 - 如果生成的源文件已编译,它将被视为顶级类,即使名称< em>看起来像<嵌套类的。
嵌套和封闭类在其字节码中相互引用。从JVM Specification, Chapter 4.7.6. The InnerClasses Attribute开始,关于 classes [] 项目:
此外,每个嵌套类和嵌套接口的 constant_pool 表必须引用其封闭类,因此,每个嵌套类和嵌套接口都将为每个封闭类和每个嵌套类提供InnerClasses信息。它自己的嵌套类和接口。
所以你可以为嵌套类创建一个.class文件(内部类的字节码结构正确),然后使用一些字节码操作库来改变封闭类的实现。
实际上,您不应该使用注释处理器更改现有类。无论如何,似乎有可能,如here所述。
所有这些似乎需要付出很多努力。如果可能的话,我会避免这一切,并尝试不同的方法。很难说出你的用例是什么,但可能会生成TestClass
的子类并将你的静态类放入子类或类似的东西。