Подтвердить что ты не робот

Можно ли заменить метод groovy для существующего объекта?

Следующий код попытался заменить существующий метод в классе 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 [email protected][name: abc params: [] returns: class java.lang.Object owner: class A]
Method public void A.abc()
new

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

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

4b9b3361

Ответ 1

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

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

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

Ответ 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 [email protected]
  [name: abc params: [] returns: class java.lang.Object owner: class A]
Method public void A.abc()
new

На основе Groovy 2.2.1. Вопрос слишком старый, хотя.

Ответ 3

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

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

Оттуда он будет действовать так, как вы хотите. Когда вы будете искать методы, вы все равно получите два:

Method [email protected][name: abc params: [] returns: class java.lang.Object owner: class A]
Method public void A.abc()

Но по крайней мере вам не нужно менять объявление public void.
Не уверен, что это ошибка или что.