我正在尝试使用元编程覆盖Java类上的私有方法 . 代码看起来像这样:
// Java class
public class MyClass{
private ClassOfSomeSort property1;
private ClassOfSomeOtherSort property2;
public void init(){
property1 = new ClassOfSomeSort();
property2 = new ClassOfSomeOtherSort();
doSomethingCrazyExpensive();
}
private void doSomethingCrazyExpensive(){
System.out.println("I'm doing something crazy expensive");
}
}
// Groovy class
public class MyClassTest extends Specification{
def "MyClass instance gets initialised correctly"(){
given:
ExpandoMetaClass emc = new ExpandoMetaClass( MyClass, false )
emc.doSomethingCrazyExpensive = { println "Nothing to see here..." }
emc.initialize()
def proxy = new groovy.util.Proxy().wrap( new MyClass() )
proxy.setMetaClass( emc )
when:
proxy.init()
then:
proxy.property1 != null
proxy.property2 != null
}
}
问题是没有调用doSomethingCrazyExpensive的重写实现 - 我认为这是因为私有方法在内部由init()方法调用而不是通过metaClass调用 . 如果我直接调用myProxy.doSomethingCrazyExpensive(),则会调用重写的方法,因此元编程在某种程度上起作用 .
有没有办法使用元编程来覆盖Java类(或实例)上的方法,以便在内部调用时调用重写的实现?
4 回答
Groovy
as
运算符功能非常强大,可以使用Java中可见的具体类型创建代理 . 可悲的是,似乎它无法覆盖私有方法,虽然我设法改变了一个公共方法:Java类:
Groovy测试:
它打印:
因此公共方法被拦截和更改,即使是从Java调用,也不是私有方法 .
您不能使用metaClass覆盖Groovy中从Java代码调用的方法 .
这就是为什么你无法在Java中“模拟”对这个私有方法的调用:它由Java类本身调用,而不是来自Groovy .
当然,如果您的类是用Groovy编写的,那么此限制将不适用 .
我建议您重构Java类,如果可以的话,您可以使用常规方法来模拟昂贵的方法调用 . 或者甚至使方法受到保护,然后在子类中覆盖它 .
我偶然发现了这个问题并认为我应该提供一个不同的答案:是的,您可以覆盖现有方法 - 您只需将元类更改为ExpandoMetaClass .
例如,当您添加第一个方法时,会自动发生这种情况 .
这是一个例子:
结果是:
您可以在添加fum方法后看到元类更改为expando . 现在,当尝试覆盖原始的foo时 - 它可以工作 .
看来你不能使用Groovy元编程来替换Java类的方法 - 甚至是公共方法 - 在Groovy控制台中尝试以下方法来确认:
如果你可以修改
MyClass
的源代码,我会使doSomethingCrazyExpensive
受保护,或者最好重构它,以便它更适合测试完成上述更改后,在测试
MyClass
时,您可以使用CrazyExpensive
的模拟/存根实现轻松实例化它 .