我的印象是闭包作为实际被调用的类运行(而不是实现超类),因此当某些变量不可见时会中断(例如超类中的私有)。
例如
package comp.ds.GenericTest2
import groovy.transform.CompileStatic
@CompileStatic
class ClosureScopeC {
private List<String> list = new ArrayList<String>()
private int accessThisPrivateVariable = 0;
void add(String a) {
list.add(a)
println("before ${accessThisPrivateVariable} ${this.class.name}")
// do something with a closure
list.each {String it ->
if (it == a) {
// accessThisPrivateVariable belongs to ClosureScopeC
accessThisPrivateVariable++
}
}
println("after ${accessThisPrivateVariable}")
}
}
// this works fine
a = new ClosureScopeC()
a.add("abc")
a.add("abc")
// child class
class ClosureScopeD extends ClosureScopeC {
void doSomething(String obj) {
this.add(obj)
}
}
b = new ClosureScopeD()
// THIS THROWS groovy.lang.MissingPropertyException: No such property: accessThisPrivateVariable for class: comp.ds.GenericTest2.ClosureScopeD
b.doSomething("abc")
最后一行抛出一个MissingPropertyException:子类调用超类的“add”方法,该方法执行“each”闭包,该闭包使用“accessThisPrivateVariable”。
我是groovy的新手,所以我认为必须有一个简单的方法来做到这一点,因为否则似乎闭包完全破坏了在超类中完成的私有实现的封装...这似乎是一个非常常见需求(超类实现引用自己的私有变量)
我正在使用groovy 2.1.3
答案 0 :(得分:0)
我发现这是一个很好的参考,描述了Groovy变量范围如何工作并适用于您的情况:Closure in groovy cannot use private field when called from extending class
从上面的链接中,我意识到由于您已将accessThisPrivateVariable
声明为私有,因此Groovy不会为变量自动生成getter / setter。请记住,即使在Java中,私有变量也不能由子类直接访问。
更改代码以显式添加getter / setter,解决了问题:
package com.test
import groovy.transform.CompileStatic
@CompileStatic
class ClosureScopeC {
private List<String> list = new ArrayList<String>()
private int accessThisPrivateVariable = 0;
int getAccessThisPrivateVariable() { accessThisPrivateVariable }
void setAccessThisPrivateVariable(int value ){this.accessThisPrivateVariable = value}
void add(String a) {
list.add(a)
println("before ${accessThisPrivateVariable} ${this.class.name}")
// do something with a closure
list.each {String it ->
if (it == a) {
// accessThisPrivateVariable belongs to ClosureScopeC
accessThisPrivateVariable++
}
}
println("after ${accessThisPrivateVariable}")
}
}
// this works fine
a = new ClosureScopeC()
a.add("abc")
a.add("abc")
// child class
class ClosureScopeD extends ClosureScopeC {
void doSomething(String obj) {
super.add(obj)
}
}
b = new ClosureScopeD()
b.doSomething("abc")
有一种更简单的方法,只需将访问修饰符(应该将属性真正重命名)设置为protected,这样子类就可以访问该属性了。问题解决了。
protected int accessThisProtectedVariable
为了澄清您对Groovy可能破坏封装的担忧声明:请放心它没有。
通过将字段声明为私有字符,Groovy通过故意暂停自动生成公共getter / setter来保留封装。一旦私有,您现在负责并完全控制子类(受保护)或所有类对象(公共)是否有方法通过显式添加方法来获取对字段的访问权限 - 如果这有意义的话。
请记住,按照惯例,当代码引用字段时,Groovy会一直调用getter或setter。所以,声明如下:
def f = obj.someField
实际上会调用obj.getSomeField()
方法。
同样地:
obj.someField = 5
将调用obj.setSomeField(5)
方法。