4

これは私がやりたいことです:

class MyObject {
    @Lazy volatile String test =  {
        //initalize with network access
    }()
}

def my = new MyObject()
println my.test

//Should clear the property but throws groovy.lang.ReadOnlyPropertyException
my.test = null 

//Should invoke a new initialization
println my.test

残念ながら、遅延フィールドは Groovy の読み取り専用フィールドであり、プロパティをクリアすると例外が発生します。

@Lazy アノテーションによって提供される二重チェックロジックを再実装せずに、遅延フィールドを再初期化可能にする方法はありますか?

アップデート:

soft=true (最初の回答から) を考慮して、いくつかのテストを実行しました。

class MyObject {
    @Lazy() volatile String test =  {
        //initalize with network access
        println 'init'
        Thread.sleep(1000)
        'test'
    }()
}

def my = new MyObject()
//my.test = null 
10.times { zahl ->
    Thread.start {println "$zahl: $my.test"}
}

約 1 秒後に、Groovy コンソールに次の出力が表示されます。

init
0: test
7: test
6: test
1: test
8: test
4: test
9: test
3: test
5: test
2: test

これは予想通り (そして希望通り) です。ここで追加するsoft=trueと、結果が劇的に変化し、10 秒かかります。

init
init
0: test
init
9: test
init
8: test
init
7: test
init
6: test
init
5: test
init
4: test
init
3: test
init
2: test
1: test

テストを間違っているか、soft=true がキャッシング効果を完全に破壊しているのかもしれません。何か案は?

4

1 に答える 1

5

soft Lazyの属性を使用できませんか、つまり:

class MyObject {
  @Lazy( soft=true ) volatile String test =  {
    //initalize with network access
  }()
}

編集

ではsoft=true、アノテーションは次のようにセッターとゲッターを生成します。

private volatile java.lang.ref.SoftReference $test 

public java.lang.String getTest() {
    java.lang.String res = $test?.get()
    if ( res != null) {
        return res 
    } else {
        synchronized ( this ) {
            if ( res != null) {
                return res 
            } else {
                res = { 
                }.call()
                $test = new java.lang.ref.SoftReference( res )
                return res 
            }
        }
    }
}

public void setTest(java.lang.String value) {
    if ( value != null) {
        $test = new java.lang.ref.SoftReference( value )
    } else {
        $test = null
    }
}

がなければsoft=true、セッターを取得できません

private volatile java.lang.String $test 

public java.lang.String getTest() {
    java.lang.Object $test_local = $test 
    if ( $test_local != null) {
        return $test_local 
    } else {
        synchronized ( this ) {
            if ( $test != null) {
                return $test 
            } else {
                return $test = { 
                }.call()
            }
        }
    }
}

したがって、変数は読み取り専用です。これが意図的なものなのか、それとも使用の副作用なのかは現在わかりませんsoft=true...

編集#2

これは、Lazy with の実装のバグのようです。soft=true

ゲッターを次のように変更すると:

  public java.lang.String getTest() {
    java.lang.String res = $test?.get()
    if( res != null ) {
      return res 
    } else {
      synchronized( this ) {
        // Get the reference again rather than just check the existing res
        res = $test?.get()
        if( res != null ) {
          return res
        } else {
          res = { 
            println 'init'
            Thread.sleep(1000)
            'test'
          }.call()
          $test = new java.lang.ref.SoftReference<String>( res )
          return res 
        }
      }
    }
  }

動作していると思います...バグ修正に取り組みます

于 2013-03-15T10:12:11.387 に答える