【问题标题】:intercepting LOCAL property access in groovy在 groovy 中拦截 LOCAL 属性访问
【发布时间】:2012-07-12 16:19:25
【问题描述】:

我在尝试在 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 对变量进行本地访问。访问方法

提前致谢!

【问题讨论】:

    标签: groovy


    【解决方案1】:

    你可以使用

    foo.@meep = new Meep()
    

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

    另一种方法是直接使用 getter 和 setter ,即

    foo.setMeep(new Meep())
    

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

    【讨论】:

    • 这将允许我从实例外部访问裸属性,而不是通过代理方法。但是,我真正想做的是能够拦截 all 访问。我想知道是否可以覆盖给定属性的 MetaProperty.setProperty 函数......或者这是否会有所帮助。
    【解决方案2】:

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

      公共无效 testAST() { def file = new File('./src/groovy/TestExample.groovy') GroovyClassLoader 调用者 = 新 GroovyClassLoader() def clazz = 调用者.parseClass(文件) def out = clazz.newInstance()

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

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

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

    -格伦

    【讨论】:

      猜你喜欢
      • 2015-08-30
      • 1970-01-01
      • 1970-01-01
      • 2013-05-23
      • 1970-01-01
      • 1970-01-01
      • 2016-12-03
      • 1970-01-01
      • 2015-02-27
      相关资源
      最近更新 更多