如何在Groovy中构建可重新初始化的惰性属性?
这就是我想做的:如何在Groovy中构建可重新初始化的惰性属性?,groovy,lazy-evaluation,Groovy,Lazy Evaluation,这就是我想做的: 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 //
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注释提供的双重检查逻辑的情况下重新初始化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完全破坏了缓存效果。有什么想法吗?你不能用
软件吗
编辑
使用soft=true
,注释将生成一个setter和一个getter,如下所示:
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
,就不会有setter
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 withsoft=true
如果我们将getter更改为:
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
}
}
}
}
public java.lang.String getTest(){
java.lang.String res=$test?.get()
如果(res!=null){
返回res
}否则{
已同步(此){
//再次获取引用,而不仅仅是检查现有的res
res=$test?.get()
如果(res!=null){
返回res
}否则{
res={
println'init'
线程。睡眠(1000)
“测试”
}.call()
$test=new java.lang.ref.SoftReference(res)
返回res
}
}
}
}
我认为它在起作用。。。我将着手解决一个错误你的建议似乎奏效了。但是,我想知道为什么,因为文档中说soft=true
使test
成为一个软引用(据我所知,它允许考虑用于GC)。那么,属性的可写性只是一个副作用吗?刚刚注意到:soft=true
似乎消除了volatile
(带双重检查锁定的线程安全)的影响Tim,我在阅读您更新的答案时尝试的是编写我自己的setter,它在内部使用$test
。这是一个黑客程序,需要内部知识,但迄今为止还有效。我想这可能是soft=true
中的一个bug,我已经更新了我的答案,并将进行更多测试。。。
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
}
}
}
}