2013-04-24 109 views
3

我有一个印象,即闭包运行时被调用的实际类(而不是实现的超类),因此当某些变量不可见时就会中断(例如private超级类)。groovy:超类中的闭包中的变量范围(MissingPropertyException)

例如

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:子类调用父类,它执行“每一个”封,它使用“accessThisPrivateVariable”的“添加”方法。

我是新来的groovy,所以我认为必须有一个简单的方法来做到这一点,因为否则似乎闭包完全打破了在超类中完成的私有实现的封装......这似乎是非常普遍的需求(超类实现引用自己的私有变量)

我使用Groovy 2.1.3

+0

至于你的其他问题,这可能是更好的在groovy用户邮件列表 – 2013-04-24 13:49:01

+0

问,好吧,谢谢蒂姆,我会这样做 – user1070984 2013-04-24 14:20:55

回答

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") 

还有一个更简单的方法,只要访问修饰符(真的应该重新命名的属性)来保护的,所以子-class有权访问该属性..问题解决了。

protected int accessThisProtectedVariable 

为了澄清您担心的问题,Groovy可能破坏了封装:请放心,它没有。

通过声明一个字段为private,Groovy通过有意中止自动生成公共getter/setter来保留封装。一旦私密化,您现在负责并完全控制子类(受保护)或所有类别对象(公共)如何通过明确添加方法来获取对该字段的访问权限,如果这是有意义的。请记住,按照惯例,当代码引用字段时,Groovy总是调用getter或setter。因此,如下语句:

def f = obj.someField 

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

obj.someField = 5 

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