使用 SPOON 创建匿名类的实例

Posted

技术标签:

【中文标题】使用 SPOON 创建匿名类的实例【英文标题】:Create instance of anonymous class with SPOON 【发布时间】:2020-12-28 20:27:29 【问题描述】:

我需要在 Spoon 中传递一个匿名类的实例作为函数参数。 在我的例子中,我有一个匿名的 Comparator,我需要将它传递给一个获取 Comparator> 作为参数的函数。

这是我的代码:

public boolean isToBeProcessed(CtType<?> candidate) 
        if(candidate instanceof CtClass<?> && ((CtClass<?>)candidate).isAnonymous()==true)
            CtClass<?> clas = (CtClass<?>)candidate;
            List<CtMethod<?>> list = clas.filterChildren(
                  new AbstractFilter<CtMethod<?>>(CtMethod.class) 
                    @Override
                    public boolean matches(CtMethod<?> method) 
                      return method.getSimpleName().equals("compare");
                    
                  
                ).list();
        return !(list==null || list.isEmpty());
        
        return false;
    
    
    @Override
    public void process(CtType<?> element) 
        // here I need to pass the anonymous Comparator class
        testComparator( ??? ); 
    

    public void testComparator(Comparator<?> comparator) 
      ......
    

我是 Spoon 的新手,非常感谢您的帮助。

谢谢。

【问题讨论】:

@Holger 我猜public void testComparator... 来自要使用处理器处理的代码(覆盖isToBeProcessedprocess)。 CtTypespoon.reflect.declaration.CtClass 而不是 javassist.CtClass @Dmytro Mitin 是对的。上面的代码来自 Spoon 的处理器类。和他写的代码一模一样。 @DmytroMitin 好的。所以它不是Javassist。但是,由于您的答案仍然没有显示,OP 如何从CtType 创建一个Comparator,以传递给testComparator,您似乎同意这不是 OP 应该做的。由于 OP 没有询问如何获得 CtNewClass&lt;?&gt;,也许您应该告诉 OP,如何处理 CtNewClass&lt;?&gt;... @user1579191 你能在处理器转换之前编写代码,在转换之后编写所需的代码吗? @Dmytro Mitin - 我需要检测系统中所有无效的比较器。只是检测它们,而不是纠正它们。我们有数百个比较器。所以我写了一个函数来处理一个比较器并告诉它是否有效。现在我想为我们系统中的每个 Comparator 调用这个函数,并将所有无效的比较器写入文件。所以我需要以某种方式将 Comparator 类或实例传递给 testComparator(),但我很难做到这一点。 【参考方案1】:

我想我设法做到了你所要求的。

dir/App.java

import java.util.Comparator;

public class App 
    public static void main(String[] args) 
        foo1(new Comparator<Integer>() 
            @Override
            public int compare(Integer o1, Integer o2) 
                return 0;
            
        );

        foo2(new Comparator<String>() 
            @Override
            public int compare(String o1, String o2) 
                return 0;
            
        );
    

src/main/java/MyProcessor.java

import spoon.Launcher;
import spoon.processing.AbstractProcessor;
import spoon.reflect.declaration.CtClass;
import spoon.reflect.declaration.CtMethod;
import spoon.reflect.declaration.CtType;
import spoon.reflect.visitor.filter.AbstractFilter;
import spoon.support.compiler.FileSystemFile;

import java.io.FileWriter;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.List;

public class MyProcessor extends AbstractProcessor<CtType<?>> 
    @Override
    public boolean isToBeProcessed(CtType<?> candidate) 
        if (candidate instanceof CtClass<?> && ((CtClass<?>) candidate).isAnonymous() == true) 
            CtClass<?> clas = (CtClass<?>) candidate;
            List<CtMethod<?>> list = clas.filterChildren(
                    new AbstractFilter<CtMethod<?>>(CtMethod.class) 
                        @Override
                        public boolean matches(CtMethod<?> method) 
                            return method.getSimpleName().equals("compare");
                        
                    
            ).list();
            return !(list == null || list.isEmpty());
        
        return false;
    

    @Override
    public void process(CtType<?> element) 
        String implementsString = element.getParent().toString()
                .replace("()", "")
                .replace("new", "implements");

        String codeA =
                "public class A \n" +
                "  public void m() \n" +
                "    Main.testComparator(new B());\n" +
                "  \n" +
                "";

        String codeB = "public class B " + implementsString;

        String fileNameA = "dir2/A.java";
        String fileNameB = "dir2/B.java";
        try (FileWriter fileWriterA = new FileWriter(fileNameA);
             FileWriter fileWriterB = new FileWriter(fileNameB)) 
            fileWriterA.write(codeA);
            fileWriterB.write(codeB);
         catch (IOException e) 
            throw new RuntimeException(e);
        

        Launcher launcher = new Launcher();
        launcher.addInputResource("dir2");
        Collection<CtType<?>> allTypes = launcher.buildModel().getAllTypes();
        CtClass<?> ctClassA = (CtClass<?>) allTypes.stream().findFirst().get();

        Object instance = ctClassA.newInstance();
        Class<?> classA = instance.getClass();
        try 
            Method method = classA.getMethod("m");
            method.invoke(instance);
         catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) 
            throw new RuntimeException(e);
        
    

src/main/java/Main.java

import spoon.Launcher;

import java.util.Comparator;

public class Main 
    public static void testComparator(Comparator<?> comparator) 
        System.out.println("test Comparator");
    

    public static void main(String[] args) 
        Launcher launcher = new Launcher();
        launcher.addInputResource("dir");
        launcher.addProcessor(new MyProcessor());
        launcher.run();
    

dir2/A.java

dir2/B.java

pom.xml

...
<dependency>
    <groupId>fr.inria.gforge.spoon</groupId>
    <artifactId>spoon-core</artifactId>
    <version>8.2.0</version>
</dependency>

还必须将目录spooned-classes 添加到类路径中。我正在使用sbt。这可以在那里指定为unmanagedClasspath in Runtime ++= Seq(file("spooned-classes")).classpath。在 Maven 中,这应该以类似方式指定。

输出:

[main] INFO spoon.Launcher - Running in NOCLASSPATH mode (doc: http://spoon.gforge.inria.fr/launcher.html).
[main] INFO spoon.Launcher - Running in NOCLASSPATH mode (doc: http://spoon.gforge.inria.fr/launcher.html).
test Comparator
[main] INFO spoon.Launcher - Running in NOCLASSPATH mode (doc: http://spoon.gforge.inria.fr/launcher.html).
test Comparator

所以Main 中的testComparator 执行了两次,对应于App 中的foo1foo2

【讨论】:

是的,它有效!这正是我需要的!非常感谢!

以上是关于使用 SPOON 创建匿名类的实例的主要内容,如果未能解决你的问题,请参考以下文章

JAVA-匿名内部类

Java中的匿名内部类及内部类的二三事

java内部类的匿名内部类

匿名内部类

匿名类的定义,使用场景和使用方式

java匿名内部类