在下面的一段代码中(取自Groovy Semantics Manual页面),为什么要使用关键字为赋值添加前缀def
?
def x = 0 def y = 5 while ( y-- > 0 ) { println "" + x + " " + y x++ } assert x == 5
该def
关键字可以被删除,这个片段将产生相同的结果.那么关键字的效果是def
什么?
它是基本脚本的语法糖.省略"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的答案是课程的标准.
正如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()
根据此页面,def
是类型名称的替换,可以简单地将其视为别名Object
(即表示您不关心类型).
就这个单一的剧本而言,没有实际的区别.
但是,使用关键字"def"定义的变量被视为局部变量,即本地脚本的本地变量.在它们前面没有"def"的变量在第一次使用时存储在所谓的绑定中.您可以将绑定视为需要在"脚本之间"可用的变量和闭包的一般存储区域.
因此,如果您有两个脚本并使用相同的GroovyShell执行它们,则第二个脚本将能够获取在第一个脚本中设置的所有变量而没有"def".
最重要的是,你真的不是每个人都想要偶然创建一个变量.在脚本中可以在赋值中创建变量,但在生产代码中,它是您可能遇到的最大的弊端之一.我不会考虑任何允许此构造在生产代码中可用的语言.
我在不止一个使用visual basic的地方采访过他们问过"任何VB文件的第一行是什么"的问题,如果你没有回答"OPTION EXPLICIT",那么访问就在那里停止了.(OPTION EXPLICIT不允许通过VB中的赋值创建变量并强制显式"暗淡")
这是一个为什么它不好的例子.如果您复制以下代码并将其粘贴到groovy脚本中,这将运行(不会使断言失败):
bill = 7
bi1l = bill + 3
assert bill == 7
它还删除了一些编译器帮助您重构的能力.例如,如果您重命名该位置,则首先使用该变量,编译器如何知道第二次使用(赋值)现在是错误而不是新变量?
像这样的废话太危险了.即使它只是你生命中的一次,它仍然会花费更多的时间,而不是在整个carreer中明确地声明变量数千次.它也变得清晰,只是它被推迟的地方,你不必猜测.
在脚本中它是相当好的,因为脚本的范围是有限的,并且它们不会被重写和重新维护,但允许创建的语言就不能用于生产.
实际上,我不认为它会表现得一样......
Groovy中的变量仍然需要声明,而不是TYPED声明,因为右侧通常包含足够的信息供Groovy键入变量.
当我尝试使用我没有用def或类型声明的变量时,我得到一个错误"没有这样的属性",因为它假设我正在使用包含代码的类的成员.