【问题标题】:Groovy 'def' keyword and scope problem in EclipseEclipse 中的 Groovy 'def' 关键字和范围问题
【发布时间】:2011-03-21 10:54:44
【问题描述】:

我正在关注一个 groovy 教程,并且有这样的代码:

def fruit = ["apple", "orange" , "pear"]    //list
def likeIt = { String fruit -> println "I like " + fruit + "s" }    //closure
fruit.each(likeIt)

Eclipse 在闭包定义行报错:

行断点:SimpleClosuresTest [line: 27] 当前范围已经 包含名称为fruit的变量 @ 第 27 行,第 14 列。

如果我从“deffruit”中省略了“def”,Eclipse 不会抱怨并且代码运行良好。

有人能解释一下这两种情况下范围的情况吗?

谢谢。

【问题讨论】:

  • 第一次阅读您的代码时,我以为它是一个脚本,然后我注意到 SimpleClosuresTest[line: 27]... 您的示例是类的一部分吗?
  • 没有明确定义类,但是我在 SimpleClosuresTest.groovy 文件中写了这个,groovyc 创建了类 SimpleClosuresTest.class

标签: eclipse groovy scope


【解决方案1】:

首先对一个 groovy 脚本进行一般性回顾:

// file: SomeScript.groovy
x = 1
def x = 2
println x
println this.x

大致编译为:

class SomeScript extends groovy.lang.Script {
  def x
  def run() {
    x = 1
    def x = 2
    println x // 2
    println this.x // 1
  }
}

在一个 groovy 脚本(粗略地说,一个没有类声明的文件)中,为未定义的变量赋值被解释为字段赋值。

您的示例尝试使用名为 fruit 的参数定义闭包。
如果您使用 def 关键字定义了 fruit,则会收到一条错误消息,因为该名称已被视为局部变量,并且您不能复制局部变量名称。
当您离开 def 关键字时,您实际上是将值分配给为脚本生成的类的字段,因此名称 fruit 可以重新定义为局部变量。

关于作用域,它很像 java...
在示例中,您可以看到 x 首先定义为字段,然后定义为 run() 方法的局部变量。这没有什么问题,您可以同时访问变量和字段。
但是一旦你定义了一个局部变量,你就不能创建重复了。

编辑 --
必须在有人误会我之前添加这个:翻译并不完全像这样(因此是“大致”)。您将值添加到脚本的绑定中,而不是字段,就像 args 用于命令行脚本或 requestsessionresponse 用于 groovlets。
但这是一个更长的故事......
好的,如果您真的想知道,请再问,我会解释得更好

编辑 2 -- 如果您需要更多信息,我不能就这样离开……

每个 groovy 脚本都有一个名为 binding 的字段、groovy.lang.Binding 的实例或其子类之一。
这个绑定基本上是一个映射,带有方法setVariablesetVariable
当您在脚本中分配值时省略 def 关键字时,您实际上是在调用 setVariable 方法,而当您执行 this.x 之类的操作时,您正在调用 getVariable 方法。
这实际上是因为类groovy.lang.Script 覆盖了方法getPropertysetProperty 以首先调用这些方法。这就是它们表现得像字段的原因。
您可能还注意到,没有与这些变量关联的类型...那是因为我们在绑定中只处理 Map
标准 groovy 脚本是使用绑定实例创建的,其中 args 设置为参数数组。
其他的,比如groovy.servlet.ServletBinding 定义了更多的变量和行为,比如阻止某些变量的赋值,或者添加一个惰性初始化能力......

那么错误背后的真正原因是......如果没有使用def关键字,fruits不是一个真正的变量。尽管如此,我相信这种行为在某种程度上类似于一个领域。

对这一切感到抱歉。 我对自己的过度简化并不满意:S

【讨论】:

  • 谢谢,这正是我现在想要的信息量。
【解决方案2】:

String 水果不应与您的 def 水果同名。 (您首先定义一个列表,然后定义一个同名字符串)

def likeIt = { String fruit -> println "I like " + fruit + "s" }    

在第二种情况下,您使用 def ateriori 定义变量的类型,因此它可以工作,但据我所知,这不是一个好习惯。

我认为你甚至不需要写->。 groovy 手册说“-> 标记是可选的,如果您的 Closure 定义使用的参数少于两个,则可以省略”,这里就是这种情况。

【讨论】:

  • 如果我用 def 或不定义水果有什么不同(考虑到范围)?我实际上正在关注这本书:amazon.com/Groovy-Domain-Specific-Languages-Fergal-Dearle/dp/… 为什么这样做是“错误的方式”(根据你的说法)?
  • "def" 是类型名称的替代品。在变量定义中,它用于表示您不关心类型。在变量定义中,必须明确提供类型名称或使用“def”替换。因此,如果您使用“def fruit”将fruit 定义为动态类型,那么在“String fruit”中将其设置为强类型变量是没有意义的(顺便说一句,这在 1 参数闭包中没有意义)。试试 def likeIt = { println "I like" + it + "s" }fruit.each(likeIt) (它是 1 个参数闭包的默认参数名称)。
  • Tnx。但仍然 - 我非常想了解这些情况下的范围。
【解决方案3】:

第二行

串果

再次使用相同的变量名“fruit”

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-04-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-01-29
    • 2014-12-28
    相关资源
    最近更新 更多