在Java中使用Groovy向类添加方法

saw*_*303 5 java groovy metaclass

我正在研究一个使用Groovy作为编译时依赖项的Java模块,我想Person在不编写Groovy代码的情况下向我的Java类中添加一个方法(例如Groovy JDK)。

在Groovy中,我会那样实现

Person.meta.doSomething = { String param -> println "do something: ${param}" }
Run Code Online (Sandbox Code Playgroud)

如何使用Java的Groovy API做到这一点?

编辑1:

到目前为止,我已经实现了以下方法,并且我已经差不多了。我实例化一个Expando类Person并注册一个MethodClosure将方法调用委托给该类中的方法的类PersonDefaultMethods

ExpandoMetaClass expando = new ExpandoMetaClass (Person.class, true, false);
expando.registerInstanceMethod ("format", new MethodClosure (new PersonDefaultMethods(), "format"));
expando.initialize ();
Run Code Online (Sandbox Code Playgroud)

PersonDefaultMethods包含我在为Expando类中声明的方法实施。

public class PersonDefaultMethods {
   public String format(String format) {
      return this.toString(); // this one gets called
   }

   public String format(Person self, String format) { // but I want this method to be called
      return self.getFirstname() + " " + self.getLastname();
    }
}
Run Code Online (Sandbox Code Playgroud)

当我知道在此上下文中执行Groovy脚本时,我可以formatPerson实例上调用该方法,但无法delegate像平常使用闭包那样访问。

编辑2:

在我的实现中,使用闭包子类或匿名闭包类的方法失败。

ExpandoMetaClass expando = new ExpandoMetaClass(Person.class, true, false);
expando.registerInstanceMethod("format", new Closure(this) {

     @Override
     public Object call(Object arguments) {
        return super.call(arguments);
     }

     @Override
     public Class[] getParameterTypes () {
        return new Class[] { String.class};
     }
  });
expando.initialize ();
Run Code Online (Sandbox Code Playgroud)

这样就可以了。谢谢。

bla*_*rag 3

您可以通过 better. 获取当前元类GroovySystem.getMetaClassRegistry().getMetaClass(Person.class);。但要模拟上述情况,您需要手动执行 Groovy 在后台为您完成的几项操作。

  • 首先,您需要一个ExpandoMetaClass(简称 EMC)。如果上面的元类不是 EMC,您需要创建一个并在注册表中注册它:ExpandoMetaClass emc = new ExpandoMetaClass(Person.class)
  • 接下来,您需要registerInstanceMethod使用字符串和闭包或元方法进行调用。上面的变体是 Closure 版本,将在后台调用另一个版本。
  • 要遵循 Groovy 约定,您需要创建一个 Closure 子类(可能是匿名的),其中包含您希望方法的签名的 doCall 方法。for 的字符串registerInstanceMethod将是方法的名称
  • 当然,您也可以利用现有的 MetaMethod 子类之一(或您自己的子类)来实现这一目标。