【问题标题】:groovy: variable scope in closures in the super class (MissingPropertyException)groovy:超类中闭包中的变量范围(MissingPropertyException)
【发布时间】:2013-04-18 02:32:45
【问题描述】:

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

例如

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

【问题讨论】:

  • 与您的其他问题一样,这可能最好在 groovy-user 邮件列表中提出
  • 好的,谢谢蒂姆,我会这样做的

标签: groovy scope closures superclass


【解决方案1】:

我发现这是描述 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 int accessThisProtectedVariable

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

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

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

def f = obj.someField

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

obj.someField = 5

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-01-13
    • 1970-01-01
    • 2015-11-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-02-08
    • 1970-01-01
    相关资源
    最近更新 更多