首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >使用Groovy MetaClass重写方法

使用Groovy MetaClass重写方法
EN

Stack Overflow用户
提问于 2009-12-18 19:55:17
回答 3查看 25.5K关注 0票数 16

我有一个POJO,它使用服务来做一些事情:

代码语言:javascript
复制
public class PlainOldJavaObject {

    private IService service;

    public String publicMethod(String x) {
        return doCallService(x);
    }

    public String doCallService(String x) {
        if(service == null) {
            throw new RuntimeException("Service must not be null");
        }
        return service.callX(x);
    }

    public interface IService {
        String callX(Object o);
    }
}

我有一个Groovy测试用例:

代码语言:javascript
复制
class GTest extends GroovyTestCase {

    def testInjectedMockIFace() {
        def pojo = new PlainOldJavaObject( service: { callX: "very groovy" } as IService )
        assert "very groovy" == pojo.publicMethod("arg")
    }

    def testMetaClass() {
        def pojo = new PlainOldJavaObject()
        pojo.metaClass.doCallService = { String s ->
            "no service"
        }
        assert "no service" == pojo.publicMethod("arg")
    }
}

第一个测试方法testInjectedMockIFace按预期工作: POJO是用IService的动态实现创建的。当调用callX时,它只返回"very groovy“。这样,服务就被模拟出来了。

但是,我不明白为什么第二种方法testMetaClass不能像预期的那样工作,而是在尝试调用服务对象上的callX时抛出NullPointerException。我以为我已经用下面这行代码重写了doCallService方法:

代码语言:javascript
复制
pojo.metaClass.doCallService = { String s ->

我做错了什么?

谢谢!

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2010-03-09 04:12:52

你的语法有点不对劲。问题是pojo是一个Java对象,没有metaClass。要使用ExpandoMetaClass拦截对PlainOldJavaObject的doCallService的调用:

只需替换:

代码语言:javascript
复制
    pojo.metaClass.doCallService = { String s ->
        "no service"
    }

通过以下方式:

代码语言:javascript
复制
    PlainOldJavaObject.metaClass.doCallService = { String s ->
        "no service"
    }
票数 20
EN

Stack Overflow用户

发布于 2009-12-29 04:59:55

如果您的POJO确实是一个Java类,而不是Groovy类,那么这就是您的问题所在。Java类不通过metaClass调用方法。例如,在Groovy中:

代码语言:javascript
复制
pojo.publicMethod('arg')

等同于这个Java:

代码语言:javascript
复制
pojo.getMetaClass().invokeMethod('publicMethod','arg');

invokeMethod通过metaClass发送呼叫。但是这个方法:

代码语言:javascript
复制
public String publicMethod(String x) {
    return doCallService(x);
}

是一种Java方法。它不使用invokeMethod来调用doCallService。要让您的代码正常工作,PlainOldJavaObject需要是一个Groovy类,这样所有调用都将通过metaClass。普通的Java代码不使用metaClasses。

简而言之:即使Groovy也不能覆盖Java方法调用,它只能覆盖来自Groovy或通过invokeMethod调度的调用。

票数 18
EN

Stack Overflow用户

发布于 2009-12-18 20:21:54

你所拥有的看起来很好。我在groovy控制台webapp上运行了一个稍微修改过的版本,它运行起来没有任何问题。您可以在http://groovyconsole.appspot.com/上亲自查看使用此代码。

代码语言:javascript
复制
public interface IService {
    String callX(Object o);
}

public class PlainOldJavaObject {

    private IService service;

    public String publicMethod(String x) {
        return doCallService(x);
    }

    public String doCallService(String x) {
        if(service == null) {
            throw new RuntimeException("Service must not be null");
        }
        return service.callX(x);
    }
}

def pojo = new PlainOldJavaObject()
pojo.metaClass.doCallService = { String s ->
    "no service"
}
println pojo.publicMethod("arg")

您使用的是什么版本的Groovy。这很可能是Groovy的元类实现中的一个bug。groovy语言的发展非常迅速,元类实现也因版本而异。

编辑-来自评论的反馈:

groovy控制台webapp的版本是1.7-rc-1。所以看起来这个版本可能会像你想要的那样工作。他们目前在RC2中,所以我预计它很快就会发布。不确定您看到的是错误还是1.6.x版本中工作方式的不同。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/1927796

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档