3

我的印象是闭包作为被调用的实际类(而不是实现的超类)运行,因此当某些变量不可见时(例如超类中的私有)会中断。

例如

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

4

1 回答 1

0

我发现这是一个很好的参考,描述了 Groovy 变量范围如何工作并适用于您的情况:Closure in groovy cannot use private field when called from extended 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 int accessThisProtectedVariable

为了澄清您对 Groovy 可能破坏封装的担忧声明:请放心,它没有。

通过将字段声明为私有,Groovy 通过有意暂停公共 getter/setter 的自动生成来保留封装。一旦私有,您现在负责并完全控制子类(受保护)或所有对象类(公共)如何或是否有办法通过显式添加方法来访问该字段 - 如果这有意义的话。

请记住,按照惯例,当您的代码引用该字段时,Groovy 总是调用 getter 或 setter。所以,像这样的声明:

def f = obj.someField

实际上会调用该obj.getSomeField()方法。同样地:

obj.someField = 5

将调用该obj.setSomeField(5)方法。

于 2016-01-15T20:09:51.477 回答