Groovy: “ def x = 0”中“ def”的用途是什么?

在下面这段代码(取自 Groovy 语义手册页)中,为什么在赋值前加关键字 def

def x = 0
def y = 5


while ( y-- > 0 ) {
println "" + x + " " + y
x++
}


assert x == 5

可以删除 def关键字,这个代码片段将产生相同的结果。那么关键字 def效果是什么呢?

93581 次浏览

事实上,我认为它会表现得一样..。

Groovy 中的变量仍然需要声明,只是不需要 TYPED 声明,因为右边通常包含足够的信息,Groovy 可以输入变量。

当我尝试使用一个没有用 def 或类型声明的变量时,我得到一个错误“ No such property”,因为它假设我使用的是包含代码的类的一个成员。

根据这个 呼叫def是类型名的替代品,可以简单地看作是 Object的别名(也就是说,表示您不关心类型)。

它是基本脚本的语法糖。省略“ def”关键字会将变量放入当前脚本的绑定中,而 groovy (大多数情况下)将其视为一个全局作用域变量:

x = 1
assert x == 1
assert this.binding.getVariable("x") == 1

相反,使用 def 关键字不会将变量放入脚本绑定中:

def y = 2


assert y == 2


try {
this.binding.getVariable("y")
} catch (groovy.lang.MissingPropertyException e) {
println "error caught"
}

指纹: “发现错误”

在大型程序中使用 def 关键字很重要,因为它有助于定义可以找到变量的范围,并有助于保持封装。

如果你在你的脚本中定义了一个方法,它就不能访问在主脚本中用“ def”创建的变量,因为它们不在作用域中:

 x = 1
def y = 2




public bar() {
assert x == 1


try {
assert y == 2
} catch (groovy.lang.MissingPropertyException e) {
println "error caught"
}
}


bar()

打印“错误捕获”

“ y”变量不在函数的作用域内。“ x”在作用域中,因为 groovy 将检查当前脚本对变量的绑定。正如我前面所说的,这只是一种语法糖,它可以使快速的脏脚本更快地输出(通常是一行)。

在较大的脚本中的一个好的实践是始终使用“ def”关键字,这样您就不会遇到奇怪的范围问题或者干扰您不想要的变量。

Ted 的回答 对于脚本来说是非常好的; 本的回答对于类来说是标准的。

正如 Ben 所说,可以把它想象成“ Object”——但是它更酷,因为它不会限制你使用 Object 方法。这对进口具有明显的影响。

在这个代码片段中,我必须导入 FileChannel

// Groovy imports java.io.* and java.util.* automatically
// but not java.nio.*


import java.nio.channels.*


class Foo {
public void bar() {
FileChannel channel = new FileInputStream('Test.groovy').getChannel()
println channel.toString()
}
}


new Foo().bar()

但在这里,只要所有内容都在类路径上,我就可以“即兴发挥”

// Groovy imports java.io.* and java.util.* automatically
// but not java.nio.*
class Foo {
public void bar() {
def channel = new FileInputStream('Test.groovy').getChannel()
println channel.toString()
}
}


new Foo().bar()

就这一个脚本而言,没有实际的区别。

但是,使用关键字“ def”定义的变量被视为局部变量,即这个脚本的局部变量。前面没有“ def”的变量在第一次使用时存储在所谓的绑定中。您可以将绑定视为变量和闭包的通用存储区域,这些变量和闭包需要在“ between”脚本中可用。

因此,如果您有两个脚本并使用相同的 GroovyShell 执行它们,那么第二个脚本将能够获取在第一个脚本中设置的所有变量,而不需要“ def”。

使用“ def”的原因是告诉 groovy 您打算在这里创建一个变量。这一点很重要,因为您永远不希望意外地创建一个变量。

这在脚本中是可以接受的(Groovy 脚本和 groovysh 允许你这样做) ,但是在产品代码中,这是你可能遇到的最大的问题之一,这就是为什么你必须在所有实际的 Groovy 代码(类中的任何东西)中定义一个带有 def 的变量。

这里有一个例子说明为什么它是坏的。如果您复制下面的代码并将其粘贴到 groovysh 中,它将运行(不会使断言失败) :

bill = 7
bi1l = bill + 3
assert bill == 7

这类问题可能需要很多时间来发现和解决——即使它在你的生活中只咬你一次,它仍然比在你的职业生涯中数千次明确地声明变量花费更多的时间。它也变得清晰的眼睛,只是它被宣布,你不必猜测。

在不重要的脚本/控制台输入(如 groovy 控制台)中,这是可以接受的,因为脚本的作用域是有限的。我认为 groovy 允许您在脚本中实现这一点的唯一原因是像 Ruby 那样支持 DSL (如果你问我,这是一个糟糕的交易,但是有些人喜欢 DSL)