187

在下面的一段代码中(取自Groovy Semantics Manual page),为什么要在赋值前加上关键字def

def x = 0
def y = 5

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

assert x == 5

def可以删除关键字,并且此代码段将产生相同的结果。那么关键字的作用def是什么?

4

6 回答 6

287

它是基本脚本的语法糖。省略“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”关键字,这样您就不会遇到奇怪的范围问题或干扰您不打算使用的变量。

于 2008-10-09T03:51:30.493 回答
38

Ted 的回答非常适合脚本;本的答案是标准的类。

正如 Ben 所说,将其视为“对象”——但它更酷,因为它不会将您限制在 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()
于 2008-10-11T16:37:21.043 回答
31

根据这个页面def是类型名称的替代品,可以简单地被认为是别名Object(即表示您不关心类型)。

于 2008-10-08T18:02:25.770 回答
14

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

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

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

于 2008-10-14T13:29:09.767 回答
9

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

这在脚本中是可以接受的(Groovy 脚本和 groovysh 允许您这样做),但在生产代码中,这是您可能遇到的最大弊端之一,这就是为什么您必须在所有实际 groovy 代码中定义一个带有 def 的变量(任何在班级)。

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

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

这类问题可能需要花费大量时间来查找和解决——即使它在你的一生中只咬过你一次,它仍然比在你的整个职业生涯中明确声明变量数千次要花费更多的时间。它在哪里被声明也变得一目了然,你不必猜测。

在不重要的脚本/控制台输入(如 groovy 控制台)中,这是可以接受的,因为脚本的范围是有限的。我认为 groovy 允许您在脚本中执行此操作的唯一原因是像 Ruby 那样支持 DSL(如果您问我,这是一个糟糕的权衡,但有些人喜欢 DSL)

于 2013-08-02T21:18:43.383 回答
5

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

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

当我尝试使用未使用 def 或类型声明的变量时,我收到错误“没有此类属性”,因为它假定我正在使用包含代码的类的成员。

于 2008-10-08T18:01:52.963 回答