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()
  }
}

ゲッターとセッターをオーバーライドして、オブジェクトの通常のプロパティスペースではなく、マップに値を配置します。要約すると、これは少しばかげていますが、データをマップに配置する代わりに、DBまたは他の便利なものに永続化していたと想像してください。

残念ながら、次のコードは機能しなくなりました。

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など)は、getPropertyを経由するのではなく、直接MetaPropertyアクセスを使用するため、以下は機能しません。

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はgetPropertyインターセプターではなくMetaProperty直接アクセス(事実上this.meep.getMyMeep())を使用するため、最後の行でnullポインター例外がスローされます。残念ながら'meep'はnullですが、getProperty('meep')はnullではありません。

要するに、私が探しているのは、次の基準を解決するための戦略です。

  • プロパティの読み取り/書き込みをインターセプトして、自動代替データストレージを有効にします
  • 他の開発者のための透明またはほぼ透明なインターフェース(私は他の人々の生活を著しく困難にしたくありません)
  • MetaProperty / this/etcを使用して変数のローカルアクセスを許可します。アクセス方法

前もって感謝します!

4

2 に答える 2

1

あなたが使うことができます

foo.@meep = new Meep()

メソッドをバイパスしてプロパティに直接アクセスするためsetPropertyfoo.meepそれでもトリガーされるので、それはあなたの問題を完全に解決するわけではありませんset/getProperty

もう1つの方法は、会議のゲッターとセッターを直接使用することです。

foo.setMeep(new Meep())

したがって、1つの統一された方法は、すべての変数をプライベートとして定義し、get / set *PropertyName*を使用することです。

于 2012-07-12T17:45:32.303 に答える
1

AST変換を使用することで、次のことができます。

  • クラスの構造をウォークし、すべてのローカルフィールドの名前をx-> xのように変更します。
  • このように名前が変更されたフィールドごとにゲッター/セッターを追加します

    def get_ x _(){ x }

... Groovyプロパティとしてではなくフィールドとしてxにアクセスするために、次のクラスに変換を適用します

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 assert out.y == 11 out.y = 5 assert out.y == 5 out.x = 2 assert out.m.containsKey('x')assert out.mx == 2assertout。私==3

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

そして、mが更新される、日付デリゲートメソッドの時刻が適切にアクセスされるなど、すべてがうまくいくはずです。

-グレン

于 2012-07-15T21:14:44.897 に答える