1

我在尝试在 Groovy 中使用属性访问时遇到了问题。参加以下课程:

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 以简单地将值放入 M​​ap 而不是放入对象的普通属性空间。抽象地说,这有点傻,但是想象一下,我们没有将数据放入映射中,而是将其持久化到数据库或其他有用的东西中。

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

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 可以同时设置 MetaProperty 和 Map 值等。但是,我想到的所有策略都需要程序员格外小心,以确保他们正在访问类中的属性他们的意思的确切方式。

此外,Groovy 中的一些内置很棒的东西(例如@Delegate)使用直接的 MetaProperty 访问而不是通过 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' 为空,但 getProperty('meep') 不会。

简而言之,我正在寻找一种解决以下标准的策略:

  • 拦截属性读/写以启用自动替代数据存储
  • 为其他开发人员提供透明或近乎透明的界面(我不想让其他人的生活变得更加艰难)
  • 允许使用 MetaProperty/this/etc 对变量进行本地访问。访问方法

提前致谢!

4

2 回答 2

1

通过使用 AST 转换,我可以执行以下操作:

  • 遍历一个类的结构并将所有本地字段重命名为 x -> x
  • 像这样为每个重命名的字段添加一个 getter/setter

    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)
  }
}
  • 现在运行这样的测试方法:

    public void testAST() { def file = new File('./src/groovy/TestExample.groovy') GroovyClassLoader invoker = new GroovyClassLoader() def clazz = invoker.parseClass(file) def out = clazz.newInstance()

    out.x = 10 断言 out.y == 11 out.y = 5 断言 out.y == 5 out.x = 2 断言 out.m.containsKey('x') 断言 out.mx == 2 断言。我的 == 3

    out.date = new Date() 断言 out.time && out.time > 0 }

一切都应该解决,包括 m 得到更新,日期委托方法时间得到正确访问等。

-格伦

于 2012-07-15T21:14:44.897 回答
1

你可以使用

foo.@meep = new Meep()

为了直接访问属性绕过setProperty方法。foo.meep尽管仍然触发,但这并不能完全解决您的问题set/getProperty

您可以采取的另一种方法是直接使用 getter 和 setter ,即

foo.setMeep(new Meep())

因此,一种统一的方法是将所有变量定义为私有并使用 get/set*PropertyName*

于 2012-07-12T17:45:32.303 回答