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


180

在下面的代码段(摘自Groovy语义手册页)中,为什么要为关键字加上前缀def

def x = 0
def y = 5

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

assert x == 5

def关键字可以被删除,这个片段将产生相同的结果。那么关键字的作用def什么?

Answers:


278

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


36

泰德(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()

1
为什么允许new FileInputStream('Test.groovy').getChannel()不进行导入?
亚历山大·苏拉菲尔

3
@AlexanderSuraphel“只要一切都在课堂上”
Hanno

30

根据此页面def可以替换类型名称,并且可以简单地将其视为别名Object(即表示您不关心类型)。


12

就此单个脚本而言,没有实际区别。

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

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


8

“ def”的原因是告诉groovy您打算在此处创建变量。这很重要,因为您永远都不想偶然创建变量。

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

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

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

这种问题可能需要花费大量时间来查找和解决-即使只对您的生活造成一次痛苦,它仍然比在整个职业生涯中数千次明确声明变量要花费更多的时间。它在声明的位置也很清晰,您不必猜测。

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


5

其实,我认为它会表现得一样的...

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

当我尝试使用未使用def或类型声明的变量时,出现错误“ No such property”,因为它假定我正在使用包含代码的类的成员。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.