Можно ли заменить заводной метод для существующего объекта? - PullRequest
20 голосов
/ 15 марта 2010

Следующий код пытался заменить существующий метод в классе 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

Означает ли это, что при изменении метакласса путем установки его на закрытие, он на самом деле не заменяет его, а просто добавляет другой метод, который может вызвать, в результате чего метакласс имеет два метода? Можно ли по-настоящему заменить метод, чтобы вторая строка вывода выводила «new»?

При попытке понять это, я обнаружил, что DelegatingMetaClass может помочь - это самый Groovy способ сделать это?

Ответы [ 3 ]

14 голосов
/ 15 марта 2010

Вы можете использовать метакласс для каждого экземпляра, чтобы изменить значение в существующем объекте следующим образом:

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"  
  }
} 

Тогда вы получите только одно замыкание в метаклассе и никакого метода после изменения

5 голосов
/ 31 декабря 2013

Я полностью согласен с @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. Вопрос слишком старый, хотя

1 голос
/ 16 марта 2013

Я уверен, что ваша все еще имеет эту проблему;) но ... Я использую Groovy версии 1.8.7 на Fedora 17. Я обнаружил, что вы должны сделать эту комбинацию:

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.
Не уверен, что это ошибка или что-то подобное.

...