Groovy语言规范之程序结构

来源:互联网 发布:java变量的三要素 编辑:程序博客网 时间:2024/06/03 07:50

前言:
官方关于Programm Structure的介绍:Programm Structure
下文将会介绍Groovy的程序结构。

1.包名 Package names

这里的包名同Java中的包名发挥着同样的角色。包名允许我们分隔代码从而避免冲突。Groovy类必须在定义之前指定他们的包,并且假设默认的包名存在。
定义包的方式和Java非常相似

// defining a package named com.yoursitepackage com.yoursite

你可以调用Foo类使用com.yoursite.com.Foo,当然下面也将介绍import声明的方式引用类。

2.导包 Imports

为了引用任意一个类,而不需要包名。Groovy遵从了Java的方式允许使用import声明来解决类的导入。
例如,Groovy提供了几个builder类,例如MarkupBuilder。MarkupBuilder类在groovy.xml包中,所有你可以使用该类,通过以下方式的导入:

// importing the class MarkupBuilderimport groovy.xml.MarkupBuilder// using the imported class to create an objectdef xml = new MarkupBuilder()assert xml != null

2.1. 默认导入 Default imports

默认导入时Groovy语言默认导入的。例如下面的代码:

 new Date() 

同样的代码在Java中则需要导包声明:java.util.Date。Groovy默认为你导入了这些类。
Groovy默认添加了以下导入:

import java.lang.*import java.util.*import java.io.*import java.net.*import groovy.lang.*import groovy.util.*import java.math.BigIntegerimport java.math.BigDecimal

之所以这样做,是因为这些类非常常用。通过默认导入,减少了代码量。

2.2. 简单导包 Simple import

简单导包是你使用类的全路径进行导包。例如下面:

// importing the class MarkupBuilderimport groovy.xml.MarkupBuilder// using the imported class to create an objectdef xml = new MarkupBuilder()assert xml != null

2.3. 星号导入 Star import

像Java一样,Groovy提供了一个特殊的方式导入包下的所有的类通过使用*,也就是所谓的星号导包。实行星号导包前的导入方式:

import groovy.xml.MarkupBuilderimport groovy.xml.StreamingMarkupBuilderdef markupBuilder = new MarkupBuilder()assert markupBuilder != nullassert new StreamingMarkupBuilder() != null

使用星号导包后:

import groovy.xml.*def markupBuilder = new MarkupBuilder()assert markupBuilder != nullassert new StreamingMarkupBuilder() != null

2.4.静态导包 Static import

在Groovy的静态导包允许你导入类之后可以向静态方法一样在你自己的类中。和Java的静态导入相似,但是在Java1.4之上才能正常工作。
用法和Java基本相似,不再赘述:

import static Boolean.FALSEassert !FALSE //use directly, without Boolean prefix!

2.5.静态导包别名 Static import aliasing

这种用法比较类似于C语言中的typedef的用法。
使用as关键字的静态导包为命名空间问题提供了一个简洁的解决方案。加入你想要获得一个Calendar实例,使用getInstance()方法。它是静态方法,所以你可以使用静态导入。如果采用静态发导入,在调用时将不带类名因此容易误导使用者。我们可以使用别名的形式进行导入,以增强阅读性。

import static Calendar.getInstance as nowassert now().class == Calendar.getInstance().class

这样变得非常简洁易读。

2.6.静态星号导入 Static star import

星号静态导入和规则的星号导入很相似。将会导入该类的所有的静态的方法。

例如,下面的例子:

import static java.lang.Math.*assert sin(0) == 0.0assert cos(0) == 1.0

2.7 别名导入 Import aliasing

使用别名,我们可以使用自定义的名称来指向一个完全的类。同上,可以通过as关键字来实现。
考虑以下类,假设由第三方的类库提供的:

package thirdpartylibpublic class MultiplyTwo {    def static multiply(def value) {        return value * 3 //intentionally wrong.    }}

假如我们这样使用这个库:

def result = new MultiplyTwo().multiply(2)

现在假设有这样一种情况,在使用了这个第三方库并且贯穿了你所有的代码,我们发现它没有给出一个正确的结果。我们怎样在一处修改,而不是修改源码,而不改变其他使用的地方?Groovy提供了简洁的方案。
使用简单的别名导入,我们可以修复这个bug像这样:

import thirdpartylib.MultiplyTwo as OrigMultiplyTwoclass MultiplyTwo extends OrigMultiplyTwo {    def multiply(def value) {        return value * 2 // fixed here    }}// nothing to change below heredef multiplylib = new MultiplyTwo()// assert passes as wellassert 4 == new MultiplyTwo().multiply(2)

这就是通过as关键字重命名导入的类解决了这个问题。
别名导入在解决星号导入时的命名冲突同样有用。

3.脚本vs类 Script versus classes

3.1. public static void main vs 脚本 public static void main vs script

Groovy同时支持class和script(即类和脚本)。使用一下代码作为例子:
Main.groovy

class Main {    //定义一个Main类,名字是任意的                                  static void main(String... args) {                  println 'Groovy world!'                     }}

你会发现以上是Java中的典型代码,代码嵌入类中执行。而Groovy是的这些变得更加容易,以下代码是等价的:
Main.groovy

println 'Groovy world!'

你可以把脚本想象成是一个不用声明的类。

3.2 脚本类 Script class

脚本总是会被编译成类。Groovy编译器会为你编译这个类,会把脚本体复制进run方法。上面的例子会被编译如下:
Main.groovy

import org.codehaus.groovy.runtime.InvokerHelperclass Main extends Script { //Main类继承了groovy.lang.Script类                        def run() { //groovy.lang.Script需要run方法返回一个值                                        println 'Groovy world!'                     }    static void main(String[] args) { //自动生成                  InvokerHelper.runScript(Main, args)         }}

如果脚本是一个文件,文件的基本名称会被用于生成脚本类。在这个例子中,如果文件名是Main.groovy,之后脚本类的会被命名为Main。

3.3. 方法Methods

定义方法在脚本中是可以的,如下:

int fib(int n) {    n < 2 ? 1 : fib(n-1) + fib(n-2)}assert fib(10)==89

你可以将方法和代码混合。生成的脚本类会将所有的方法带入脚本类,并且嵌入所有的脚步体到run方法中:

println 'Hello'  //脚步开始处                               int power(int n) { 2**n }    //一个没有脚本体的方法                   println "2^6==${power(6)}"//脚本   

以上代码会被转化成:

import org.codehaus.groovy.runtime.InvokerHelperclass Main extends Script {    int power(int n) { 2** n}//power方法被复制进了自动生成的脚本类里面                       def run() {        println 'Hello'      //脚本被复制到了这里面                           println "2^6==${power(6)}"                  }    static void main(String[] args) {        InvokerHelper.runScript(Main, args)    }}

尽管Groovy通过你的脚本创建了一个类,不过这个类对用户来说依然是易懂的。特别的,脚本被编译成字节码,行号被保留。这就意味着如果脚本中出现了一个异常,异常记录将会返回相应的原始脚本的行号,而不是你自动生成类的行号。

3.4. 变量Variables

变量在脚本中不需要类型声明。这就意味着,如下脚本:

int x = 1int y = 2assert x+y == 3

等同于:

x = 1y = 2assert x+y == 3

然而,二者之间在语义学上是不同的,表现如下:

  • 如果声明变量如同第一个例子,它是本地变量。将会被声明在run方法,在脚本外部不可用。特别的,这种变量在脚本的其他方法中不可见。
  • 如果变量没有被声明,变量会被绑定到脚本。并且对其他方法可见,你使用脚本与其他程序交互需要传递数据在脚本和程序之间,这种声明是非常有用的。

如果你希望变量变成一个类的字段而不是在Binding中,你可以使用@Field annotation.

1 0
原创粉丝点击