2012-07-12 89 views
1

我在尝试在Groovy中使用属性访问时遇到问题。看看下面的类:在groovy中拦截LOCAL属性访问

class Foo { 
    Map m = [:] 
    String bar 

    void getProperty(String name) { 
    m.get name 
    } 

    def setProperty(String name, value) { 
    m.set name, value 
    } 

    String getBarString() { 
    return bar // local access, does not go through getProperty() 
    } 
} 

它覆盖getter和setter简单地放置值成地图,而不是为对象的普通财产的空间。抽象地说,这有点愚蠢,但想象一下,不是将数据放入地图中,而是将其保存到数据库或其他有用的东西中。

不幸的是,下面的代码,现在将不起作用:

foo = new Foo() 
foo.bar = "blerg" // using foo.bar invokes the setProperty interceptor 
assert foo.bar == "blerg" // this will work fine as foo.bar here uses the getProperty interceptor 
assert foo.getBarString() == "blerg" // explosion and fire! getBarString accesses bar locally without going through the getProperty interceptor so null will actually be returned. 

当然有这个解决方法,可能的setProperty都设置在元属性和Map值等。然而,所有我的策略我们曾经想过要求程序员特别小心,以确保他们正确地访问类属性。

此外,一些建在真棒东西在Groovy(如@Delegate例如)使用直接元属性访问,而不是经历的getProperty所以下面永远不会成功:

class Meep { 
    String getMyMeep() { 
    return "MEEP!!!" 
    } 
} 

class Foo { 
    Map m = [:] 
    String bar 
    @Delegate Meep meep 

    void getProperty(String name) { 
    m.get name 
    } 

    def setProperty(String name, value) { 
    m.set name, value 
    } 

    String getBarString() { 
    return bar 
    } 
} 

foo = new Foo() 
foo.meep = new Meep() // uses setProperty and so does not place the Meep in the Map m 
foo.getMyMeep() 

一个空指针异常是抛出最后一行,因为@Delegate使用MetaProperty直接访问(有效地this.meep.getMyMeep()而不是getProperty拦截器。不幸的是,'meep'为null,尽管getProperty('meep')不会。

In总之我在寻找的是一个解决以下标准的策略:

  • 拦截属性读/写启用自动替代数据存储为其他开发商
  • 透明或接近透明的界面(我不想让其他人的生活显著困难)
  • 允许本地使用MetaProperty/this /等访问变量。访问方法

在此先感谢!

回答

1

通过使用AST转型,我可以做到以下几点:

  • 走一类的结构和重新命名所有的本地领域的东西,比如x - >X
  • 添加getter /设定器对于这样

    每个重命名字段DEF的get_ X _(){ X }

...以便访问X作为字段而不是Groovy属性 - 现在将转换应用于以下类

class Foo { 

    def x 
    def y 
    Map m = [:] 
    @Delegate Date date // for testing if non-local fields work 

    def getProperty(String name) { 
    if (this.respondsTo("get__${name}__")) // if this is one of our custom fields 
     return "get__${name}__"() 
    "get${Verifier.capitalize(name)}"() // pass to specific getter method 
    } 

    void setProperty { 
    if (this.respondsTo("set__${name}__")) { 
     "set__${name}__"(value) 
     m[name] = value 
     if (name == "x") y = x + 1 
     return 
    } 
    "set${Verifier.capitalize(name)}"(value) 
    } 
} 
  • 现在运行的测试方法是这样的:

    公共无效testAST(){ DEF文件=新的文件( './ SRC /常规/ TestExample.groovy') GroovyClassLoader调用=新GroovyClassLoader() DEF clazz所= invoker.parseClass(文件) DEF出= clazz.newInstance()

    out.x = 10 断言out.y == 11 out.y = 5 断言out.y == 5 out.x = 2 断言out.m.containsKey( 'X') 断言out.mx == 2 断言out.my == 3

    out.date =新日期() 断言的。时间& & out.time> 0 }

,一切都应该制定出有m个得到更新,最新的委托方法时间去正确地访问等

-Glenn

1

你可以以直接访问性能绕过setProperty方法使用

[email protected] = new Meep() 

。 虽然foo.meep仍然触发set/getProperty,但这并不能完全解决您的问题。

的另一种方式,你可以通过使用getter和直接的满足二传手去的是,即

foo.setMeep(new Meep()) 

所以,一个统一的方法是定义所有的变量为私有的和使用的get/set *属性名*

+0

这将允许我从实例外部访问裸属性,而不是通过代理方法。但是,我真正想要做的是能够拦截**所有**访问。 我不知道是否有可能重写给定属性的MetaProperty.setProperty函数......或者甚至可以帮助。 – 2012-07-13 00:00:51