首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >覆盖Groovy中的metaClass属性

覆盖Groovy中的metaClass属性
EN

Stack Overflow用户
提问于 2018-06-07 23:52:50
回答 1查看 711关注 0票数 3

我想要覆盖metaClass上的动态属性,但它并不像我期望的那样工作。这里有一个简单的例子来演示这个问题。你可以在Groovy web console上在线运行它

代码语言:javascript
复制
class Bike {}
class Bell { def ring() { println("calling " + this) } }

def createNamespaces = {
    return [ "bell": new Bell() ]
}

def resetNamespaces = { bike ->
    // setting to null means 'set it to the default'
    bike.metaClass = null

    createNamespaces().each { name, namespace ->
        println("setting " + namespace)
        bike.metaClass."$name" = namespace
    }
}

def bike= new Bike()

resetNamespaces(bike)
bike.bell.ring()
resetNamespaces(bike)
bike.bell.ring()

结果是:

代码语言:javascript
复制
setting Bell@14e9bd2b
calling Bell@14e9bd2b
setting Bell@948a7ad
calling Bell@14e9bd2b

因此,尽管我更改了metaClass上的属性,但调用它始终会返回设置的第一个对象。有没有某种缓存?

当我将示例的最后一部分更改为:

代码语言:javascript
复制
resetNamespaces(bike)
resetNamespaces(bike)
bike.bell.ring()

那么结果就和预期的一样。也就是说,调用该属性将返回在metaClass上设置为最后一个的对象:

代码语言:javascript
复制
setting Bell@5b47e0c7
setting Bell@19f373a4
calling Bell@19f373a4

我甚至尝试手动设置metaClass,如下所示

代码语言:javascript
复制
def resetNamespaces = { bike ->
    def newMetaClass = new ExpandoMetaClass(Bike.class, true, true)
    newMetaClass.initialize()

    bike.metaClass = newMetaClass

    ...
}

但结果仍然是一样的。所以一定涉及到一些缓存机制。我在文档中找不到有关此行为的任何内容。

EN

回答 1

Stack Overflow用户

发布于 2018-06-25 05:09:42

产生混淆的原因是您试图更改元类的属性,而不是方法。正如groovy metaprogramming官方文档中所述,属性是通过元类的setAttribute/getAttribute方法访问的。

代码语言:javascript
复制
// throws MissingFieldException!
def resetNamespaces = { bike ->
    createNamespaces().each { name, namespace ->
        bike.metaClass.setAttribute(bike, name, namespace)
    }
}

不幸的是,只有当属性在原始的类定义中时,这才能起作用,这里它抛出了一个MissingFieldException: No such field: bell for class: Bike

另一方面,重写方法就像一个护身符,通过添加一个动态的getter方法来提供所需的语法:

代码语言:javascript
复制
def resetNamespaces(bike) {
    createNamespaces().each { name, namespace ->
        bike.metaClass."get${name.capitalize()}" = { namespace }
    }
}

def bike = new Bike()
resetNamespaces(bike)
bike.bell.ring()
resetNamespaces(bike)
bike.bell.ring()

实际上,扩展也运行得很好。

代码语言:javascript
复制
class Bell {
    def ring() { println this }
}

def bike = new Expando()
bike.createNamespaces = {
    return [ bell : new Bell() ]
}

bike.resetNamespaces = {
    bike.createNamespaces().each { name, namespace ->
        bike."$name" = namespace
    }
}

bike.resetNamespaces bike
bike.bell.ring()
bike.resetNamespaces bike
bike.bell.ring() 
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/50745518

复制
相关文章

相似问题

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