【问题标题】:Is it possible to replace groovy method for existing object?是否可以为现有对象替换 groovy 方法?
【发布时间】:2011-01-27 13:03:54
【问题描述】:

以下代码尝试替换 Groovy 类中的现有方法:

class A {
  void abc()  {
     println "original"
  }
} 

x= new A()
x.abc()
A.metaClass.abc={-> println "new" }
x.abc()
A.metaClass.methods.findAll{it.name=="abc"}.each { println "Method $it"}

new A().abc()

它会产生以下输出:

original
original
Method org.codehaus.groovy.runtime.metaclass.ClosureMetaMethod@103074e[name: abc params: [] returns: class java.lang.Object owner: class A]
Method public void A.abc()
new

这是否意味着当通过将元类设置为闭包来修改元类时,它并没有真正替换它,而只是添加了另一个它可以调用的方法,从而导致元类有两个方法?是否可以真正替换该方法,以便第二行输出打印“新”?

当我试图弄清楚时,我发现DelegatingMetaClass 可能会有所帮助——这是最时髦的方法吗?

【问题讨论】:

    标签: groovy metaprogramming


    【解决方案1】:

    您可以使用每个实例的元类来更改现有对象中的值,如下所示:

    x= new A()
    x.abc()
    x.metaClass.abc={-> println "new" }
    x.abc()
    x.metaClass.methods.findAll{it.name=="abc"}.each { println "Method $it"}
    

    但是正如你所看到的,x 将有两个与之关联的方法(实际上,一个方法和你添加的闭包

    如果你改变 A 的定义,使方法变成像这样的闭包定义:

    class A {
      def abc = { ->
         println "original"  
      }
    } 
    

    那么你只会在元类中得到一个闭包,修改后没有方法

    【讨论】:

    • 谢谢 - 我正在尝试使用它来覆盖现有 Groovy 类中的方法,因此我试图避免修改原始类。
    【解决方案2】:

    我完全同意@tim_yates 的观点。但是,有一种解决方法,如果您想避免修改原始类,请改用MethodClosure,如下所示:

    class A {
      void abc()  {
         println "original"
      }
    } 
    
    x = new A()
    
    //Create a Method Closure or Method pointer
    pointer = x.&abc
    
    //Replace Original call with Method pointer
    //x.abc()
    pointer()
    
    //Meta classed
    A.metaClass.abc={-> println "new" }
    
    //Call method pointer instead of original again
    //x.abc()
    pointer()
    
    A.metaClass.methods.findAll{it.name=="abc"}.each { println "Method $it"}
    
    new A().abc()
    

    你应该得到预期的结果:

    original
    new
    Method org.codehaus.groovy.runtime.metaclass.ClosureMetaMethod@43094309
      [name: abc params: [] returns: class java.lang.Object owner: class A]
    Method public void A.abc()
    new
    

    基于 Groovy 2.2.1。不过这个问题太老了。

    【讨论】:

    • 在这里努力理解MethodClosure。使用这种方法是否可以让 A 的两个实例(比如 x 和 y)每个都有自己独立的方法 abc?
    • 没有。一旦您使用Class A 上的元类修改了abc,那么它适用于A 的所有实例。MethodClosure 只是可以在任何时间点调用的底层方法的闭包表示。
    • 好的 - 所以不可能有多个 Class A 实例,每个实例都使用不同的方法 abc。无赖。
    • 你可以,但是每次你都必须修改元类并做A.metaClass.abc={-> println "brand new" },如果你想看到每个新实例的“全新”。有意义吗?
    • 好的 - 所以在更改元类之前创建的实例使用它们创建时的方法。更改元类后创建的实例使用新方法。如果我愿意,我可以在每次创建实例之前更改元类。我明白了吗?
    【解决方案3】:

    我确定你的仍然有这个问题;)但是......我在 Fedora 17 上运行 Groovy 1.8.7 版。我发现你必须这样做:

    A.metaClass.abc = {-> println it}
    A obj = new A()
    obj.metaClass.abc = {-> println it}
    obj.abc
    

    从那里开始,它会按照您的意愿行事。当你寻找方法时,你仍然会得到两个:

    Method org.codehaus.groovy.runtime.metaclass.ClosureMetaMethod@103074e[name: abc params: [] returns: class java.lang.Object owner: class A]
    Method public void A.abc()
    

    但至少您不必更改您的public void 声明。
    不确定这是错误还是什么。

    【讨论】:

      【解决方案4】:

      如果您只是在运行时拥有实例:

      // save original method
      def savedAbcMetaMethod = A.metaClass.getMetaMethod('abc', [] as Class[])
      
      // Override Abc Method
      A.metaClass.abc = {
          // Do something else here
          // ...
          // Method is called on instance (provide context with delegate)
          savedAbcMetaMethod.invoke(delegate)
          // ...
          // Perhaps more code here
      }
      

      MetaMethod 在 Groovy 文档中。

      我从here得到这个答案。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2019-02-16
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-03-12
        • 1970-01-01
        相关资源
        最近更新 更多