作用域和“def”的语意

:!:注意: 此页面可能跟Java语言规范(JLS)的解释有些许不同。

Java的作用域有两个基本的非正式原则:

原则1: 一个变量仅在定义它的代码块或者嵌套的代码块中是可见的。
原则2: 一个变量的可见性是唯一的。

Java可以区分类级别的变量(即类的成员变量)和局部的变量:局部变量在方法参数或者方法代码块内部定义,类级别的变量被定义为类的属性。显而易见地Java违反了我上述所描述的第一个原则,因为如果类变量被定义为公共的话,我们在类外边可以访问这些变量。如果局部变量名跟属性名(类变量)相同的话,并没有违反上述第二原则,因为此属性已经被局部变量隐藏了,并且局部变量没有“this”这个限定词,因此可以保证不被可见。

那Groovy是怎样的呢?

在Groovy中,我们也遵循这两个原则,但是因为我们有不同的模型,因此我们可以通过另外不同的方式来处理这些原则。让我们先从局部变量开始吧 :)

  • 在Groovy中你不可以定义两个同名的局部变量,就如Java那样。
  • 你可以定义一个同名的局部变量来隐藏其属性,也如Java那样。

那么区别在哪里呢?

脚本!当你在脚本里边定义一个变量的时候,它总是局部的,但是方法并不是此作用域的一部分(即方法不是局部的,译者注)。因此如果这些变量如果是属性,那么在方法中就可以使用这些变量,但当使用在脚本中定义的这些变量的时候,必然要导致错误 (英文原意好像不对,译者注),示例如下:
在脚本中不允许定义的示例

String attribute = "bar"
void aMethod(){
  assert attribute == "bar" // Not allowed !
}
aMethod()

执行这段代码将导致一个关于缺少属性或者字段的异常。

方法中唯一可以访问的有:

  • 绑定(binding)
  • 定义在基类里的属性
  • 定义在MetaClass中的动态属性(以后将会做出解释)

示例中的”attribute”既不是字段、属性,也不是动态定义的属性更不是绑定的一部分。

那变量什么时候在绑定中,什么时候不在呢?

很简单!当它没有被定义的时候,它就在绑定中。

String localVar = "I am a local variable"
bindingVar = "I am a binding variable"

此处技巧(坦白地讲,这对Java程序员来说并不容易)是:在使用前不要定义这个变量,并且此变量将被放到绑定中,而任何被定义过得变量都是局部变量。 :!:请注意:绑定仅仅存在脚本中

传说中的“def”是什么呢?

“def”是类型名称的替代品,在变量的定义中它常常用来表示你根本不需要关心变量的类型。 变量的定义必须明确的手工提供类型名称或者使用“def”来代替,这对于Groovy解析器能监测到变量定义来说,是必须的。

这些定义可以在脚本的局部变量中,也可以在类的局部变量或者属性/字段中。

:-) 经验小贴士 你可以将“def”当作“Object”来对待,那样你就会马上理解了 ^_^

Groovy在将来可能会根据静态还是动态来给出其额外的意义,但是目前发布的Groovy只是1.0。 “def”在一个方法的定义中,也可以替代返回类型为“void”。

def dynamic  =  1
dynamic = "I am a String stored in a variable of dynamic type"
int typed = 2
typed = "I am a String stored in a variable of type int??"    // throws ClassCastException

将字符串赋值给int类型的变量,当然会出错了,但是用“def”来定义就可以了。

闭包即代码块

原则上来说闭包就是一个代码块,定义在代码块中的变量只在此代码块和嵌套在此代码块的代码块可见(有点拗口哦^_^)。比如在Java中:
Java代码块

{
  int i=1;
  {
    System.out.println (i);
  }
}

这样一个代码块可以被任意定义在上述示例中,或者循环语句、同步语句、try-catch、 switch等等凡是以“{”开始的代码中。

在Groovy中,我们还要另一个“{”结构:闭包,继续上述原则,在一个闭包中是不可以定义两个同名的变量的。

禁止定义

def closure = { int i; int i }

同时内嵌的闭包中也不可以同名。
无效的两次定义

def outer = {
  int i
  def inner = { int i }
}

一个代码块必须以对于的“}”结束,因此可以在不同的代码块中重用相同的名字。

允许定义

def closure1 = { parameter ->
  println parameter
}
def closure2 = { parameter ->
  println parameter
}

以上两个闭包虽然都定义了局部变量:“parameter”, 但由于他们并不是嵌套的,因此是允许的。

:!:注意: 跟Groovy的前期版本和PHP不一样的是,变量只在代码块内是可见的,在之外是不可见的,这点很像Java。

“it”是?

“it” 是一个特殊的变量名称,它在闭包中是自动定义的。它总是指向闭包的第一个参数,或者null(当闭包没有参数的时候。

闭包中隐含着“it”

def c = { it }
assert c()  == null
assert c(1) == 1

当使用内嵌闭包(闭包中的闭包)时,“it”的含义取决于你在那个闭包内。

def outer = {
  def inner = { it+1 }
  inner(it+1)
}
assert outer(1) == 3

如你所见,“it”被使用了两次:“inner”中的“it”意味着闭包“inner”的第一个参数,而接下来的那个“it”是“outer”的第一参数。这对于拷贝包含闭包的代码来说,还是很有裨益的。

关键字“static”

“static”是属性(还有方法,在此不做讨论)的修饰符。它定义的是“静态作用域”,这意味所有没有用“static”定义的变量不是静态作用域的一部分,并且是不可见的,在Groovy中也不例外,详细的解说可以参考任何关于Java的书籍。

 
wiki/user_guide/scoping_and_the_semantics_of_def.txt · 最后更改: 2008-04-19 13:50 (外部编辑)
 
Recent changes RSS feed Creative Commons License Donate Powered by PHP Valid XHTML 1.0 Valid CSS Driven by DokuWiki