2012-07-12 94 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