Можно ли заменить метод 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 сделать это?
Ответы
Ответ 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
.
Не уверен, что это ошибка или что.