Groovy探索之爬出metaClass陷阱

这两天掉入了一个Groovy metaClass的陷阱,好不容易才爬出来。

为了说清楚这个问题,先看几行测试代码

 

    void testMetaClass() {
        def domain = new Domain()
        domain.metaClass.p = 'v1'
        assertEquals 'v1', domain.p

        domain.p = 'v2'
        assertEquals 'v2', domain.p

        domain.metaClass.p = 'v3'
        assertEquals 'v2', domain.p
    }

这是一个已经通过了的测试:

-------------------------------------------------------
Running 1 unit test...
Running test com.grs.GroovyMetaClassTests...null
Empty test suite.
PASSED
Tests Completed in 359ms ...
-------------------------------------------------------

注意Groovy测试代码的最后两行:

domain.metaClass.p = 'v3'
assertEquals 'v2', domain.p

乍一看挺让人纳闷的: 执行

domain.metaClass.p = 'v3'

之后,为什么domain.p的值仍然是'v2'呢?这就是我称之为“陷阱”的地方了。看来Groovy中似乎有这么一条规则:往GroovyObject中第一次通过

domain.metaClass.aProperty = aValue 

的方式往domain中注入aProperty之后, 再执行

domain.metaClass.aProperty = yetAnotherValue 

则无法更改domain.aProperty,而

domain.aProperty = yetAnotherValue 

却可以成功更改。

 

爬出这个陷阱,让我们来看这个问题该如何解决:我怎么才能知道aProperty有没有已经注入呢?换言之,我怎么知道我是应该写成

domain.metaClass.aProperty = yetAnotherValue

呢,还是应该写成

domain.aProperty = yetAnotherValue

呢?

 

我有两个workarounds:

1. 通过try...catch...来检测

class GroovyFieldAccessor {
  static void set(String fieldName, Object obj, Object v) {
    def metaPropAlreadyInjected = true
    try {
        if(obj."$fieldName") {} // do nothing but check if d."$fieldName" will throw an exception.
    } catch(x) {metaPropAlreadyInjected = false}
    if(metaPropAlreadyInjected) {
        obj."$fieldName" = v
    } else {
        obj.metaClass."$fieldName" = v
    }

  }
}

2. 每次都两句一起写:

obj.metaClass."$fieldName" = v
obj."$fieldName" = v

可保万无一失。

 

个人比较喜欢后者,因为它就两行,够简洁。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值