如何使用通过常规AST转换创建的内部(嵌套)类?

时间:2016-07-20 01:02:35

标签: java groovy annotations classpath abstract-syntax-tree

我正在尝试使用groovy编写一个注释和ASTTransformation来创建一个内部类。

以下是注释:

import org.codehaus.groovy.transform.GroovyASTTransformationClass;

import java.lang.annotation.*;

@Retention(RetentionPolicy.SOURCE)
@Target(ElementType.TYPE)
@GroovyASTTransformationClass("WithInnerClassTransformation")
public @interface WithInnerClass {
  public String name();
}

转型:

import org.codehaus.groovy.ast.*;
import org.codehaus.groovy.control.*;
import org.codehaus.groovy.transform.*;

@GroovyASTTransformation(phase = CompilePhase.CANONICALIZATION)
public class WithInnerClassTransformation implements ASTTransformation {
  public void visit(ASTNode[] nodes, SourceUnit source) {
    ClassNode outer = nodes[1];
    String name = nodes[0].getMember('name').text;
    def innerClassNode = new InnerClassNode(
        outer,
        "$outer.name\$$name",
        ClassNode.ACC_PUBLIC,
        new ClassNode(GroovyObject),
    );
    def compilerConfiguration = source.getAST().getUnit().config;
    def compilationUnit = new CompilationUnit(compilerConfiguration);
    compilationUnit.addClassNode(innerClassNode);
    compilationUnit.compile();
  }
}

以上简单用法:

@WithInnerClass(name = "InnerClass")
public class OuterClass {}

使用以下脚本,我确认可以通过系统类加载器访问生成的类。

def classLoader = ClassLoader.getSystemClassLoader()
Class inner = classLoader.loadClass("OuterClass\$InnerClass")
assert inner.simpleName == "InnerClass"
assert OuterClass.declaredClasses.toList().contains(inner)
println OuterClass.InnerClass.class

但是,当我尝试显式使用该类时,收到此错误消息:

Exception in thread "main" groovy.lang.MissingPropertyException: No such property: InnerClass for class: OuterClass
    at groovy.lang.MetaClassImpl.invokeStaticMissingProperty(MetaClassImpl.java:997)
    at groovy.lang.MetaClassImpl.getProperty(MetaClassImpl.java:1852)
    at groovy.lang.MetaClassImpl.getProperty(MetaClassImpl.java:1828)
    at groovy.lang.MetaClassImpl.getProperty(MetaClassImpl.java:3735)
    at org.codehaus.groovy.runtime.callsite.ClassMetaClassGetPropertySite.getProperty(ClassMetaClassGetPropertySite.java:48)
    at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callGetProperty(AbstractCallSite.java:291)
    at main.run(main.groovy:5)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:90)
    at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:324)
    at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:1208)
    at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:1017)
    at org.codehaus.groovy.runtime.InvokerHelper.invokePogoMethod(InvokerHelper.java:914)
    at org.codehaus.groovy.runtime.InvokerHelper.invokeMethod(InvokerHelper.java:897)
    at org.codehaus.groovy.runtime.InvokerHelper.runScript(InvokerHelper.java:407)
    at org.codehaus.groovy.runtime.InvokerHelper$runScript.call(Unknown Source)
    at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:45)
    at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:108)
    at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:128)
    at main.main(main.groovy)

此外,尝试使用导入会导致此错误:

org.codehaus.groovy.control.MultipleCompilationErrorsException: startup failed:
main.groovy: 1: unable to resolve class OuterClass.InnerClass
 @ line 1, column 1.
   import OuterClass.InnerClass
   ^

1 个答案:

答案 0 :(得分:0)

看来,为了明确使用生成的类,需要额外的几轮编译。要使此代码正确运行(甚至能够导入生成的类),请分3步进行编译。

groovyc WithInnerClass.groovy WithInnerClassTransformation.groovy
groovyc OuterClass.groovy
groovyc main.groovy

WithInnerClass.groovy和WithInnerClassTransformation.groovy必须与OuterClass.groovy分开编译,才能生成OuterClass $ InnerClass.class文件。

此外,OuterClass.groovy必须与main.groovy分开编译。虽然在这种情况下OuterClass $ InnerClass.class将出现在类路径中,但由于我不清楚的原因,这不足以能够在main.groovy中显式使用InnerClass。单独编译main.groovy,groovyc能够提取“真正的”类文件。