译文:Groovy Language Documentation
主目录见:Android高级进阶知识(这是总目录索引)
这章将会覆盖所有Groovy编程语言中的程序结构。
1.3.1包名
包名和java中的包名用法是一样的,它允许我们对代码进行分包而不会有冲突。Groovy 必须在类定义之前明确它的包名,不然就会使用默认包名。
定义包的方式和java一样:
// defining a package named com.yoursite
package com.yoursite
想要引用com.yoursite.com
包中的类Foo
,你需要指定全类名com.yoursite.com.Foo
,或者你可以使用import
来声明。
1.3.2Imports(导入)
为了引用一个类你必须明确引用一个包,Groovy允许你使用java的关键字import
来引用你的类。
例如,Groovy 提供了几个builder类,如MarkupBuilder
,MarkupBuilder
类在包groovy.xml
中,所以要使用这个类,你必须这样使用import
:
// importing the class MarkupBuilder
import groovy.xml.MarkupBuilder
// using the imported class to create an object
def xml = new MarkupBuilder()
assert xml != null
默认导入
默认导入的意思是Groovy语言中默认已经导入了这些包,例如这些代码:
new Date()
在java中,这样的代码是需要这样导入Date
类的:import 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.BigInteger
import java.math.BigDecimal
之所以这样做是因为这些是比较通用的,这样避免了一些样板代码。
简单导入
简单导入的意思是你导入类的时候加上了完整的包名,例如你使用import
导入import groovy.xml.MarkupBuilder
就是一个简单导入,直接引用了包中的某一个类:
// importing the class MarkupBuilder
import groovy.xml.MarkupBuilder
// using the imported class to create an object
def xml = new MarkupBuilder()
assert xml != null
星形(*)导入
Groovy跟java一样,提供了一个简单的方式(*)来引用包中的所有类,例如MarkupBuilder
类在包groovy.xml
中,同时另外一个类StreamingMarkupBuilder
也在这个包中,如果你想同时使用这个包中的这两个类的话可以这么做:
import groovy.xml.MarkupBuilder
import groovy.xml.StreamingMarkupBuilder
def markupBuilder = new MarkupBuilder()
assert markupBuilder != null
assert new StreamingMarkupBuilder() != null
这当然是正确的代码,但是使用*
导入的话,你只需要一行,就导入了包中groovy.xml
所有的类:
import groovy.xml.*
def markupBuilder = new MarkupBuilder()
assert markupBuilder != null
assert new StreamingMarkupBuilder() != null
*
导入有一个问题是他会打乱你的本地命名空间,但是用Groovy的别名很容易解决这个问题。
静态(static)导入
Groovy的静态导入引用类跟你引用本类的静态方法一样:
import static Boolean.FALSE
assert !FALSE //use directly, without Boolean prefix!
这个跟java中的静态导入一样,但是Groovy中会更具动态特征,它允许你定义相同名字的方法,只要方法参数类型不一样:
import static java.lang.String.format 1
class SomeClass {
String format(Integer i) { 2
i.toString()
}
static void main(String[] args) {
assert format('String') == 'String' 3
assert new SomeClass().format(Integer.valueOf(1)) == '1'
}
}
1.静态导入方法
2.声明和静态导入的方法一样的名字,但是参数类型不一样
3.在java中会编译错误,但是在Groovy中会通过
如果你的参数类型一样,那么导入类的优先级会更高
静态导入别名
静态导入使用as
关键字来优雅地解决命名空间问题,假如你想使用getInstance()
方法来获得Calendar
实例,这是个静态方法,所以我们要使用静态导入,但是为了避免每次都调用getInstance()
方法,而且和他的类分开的话容易引起误导,所以我们可以使用别名增加代码的可读性:
import static Calendar.getInstance as now
assert now().class == Calendar.getInstance().class
这样代码就很干净了。
静态星形导入
静态星形导入和通常的星形导入非常相似,他将会导入指定类中的所有静态方法。
例如,我们需要在我们的应用中计算sines 和cosines 值,在java.lang.Math
中有我们需要的方法名sin
和cos
,通过我们的静态星形导入,我们可以这么做:
import static java.lang.Math.*
assert sin(0) == 0.0
assert cos(0) == 1.0
正如我们所看到的,我们直接使用sin
和cos
方法,而不需要添加Math.
前缀.
导入别名
通过类型别名,我们可以选择一个别名来命名我们引用的指定类名,跟之前一样,这也可以通过as
关键字来做。
例如你能将java.sql.Date
作为SQLDate
导入,而且能在同一个文件里面使用而不需要使用类的全限定名。
import java.util.Date
import java.sql.Date as SQLDate
Date utilDate = new Date(1000L)
SQLDate sqlDate = new SQLDate(1000L)
assert utilDate instanceof java.util.Date
assert sqlDate instanceof java.sql.Date
1.3.3.脚本和类对比
public static void main 和脚本
Groovy支持脚本和类,下面是示例代码:
Main.groovy
class Main { 1
static void main(String... args) { 2
println 'Groovy world!' 3
}
}
1.定义一个Main
类,名字可以随意
2.public static void main(String[])
方法和类中的main
方法用法一致
3.main方法的主体
这个是能在java代码中找到的典型代码,需要放进类中才可以执行,Groovy使他变得简单化了,下面代码是等价的:
Main.groovy
println 'Groovy world!'
一个脚本被认为是一个类,而不需要去声明他。
脚本类
一个脚本总是被编译成一个类,Groovy的编译器会为你编译它,脚本内容会被拷贝进一个run
方法里面,之前的例子会被编译成如下代码:
Main.groovy
import org.codehaus.groovy.runtime.InvokerHelper
class Main extends Script { 1
def run() { 2
println 'Groovy world!' 3
}
static void main(String[] args) { 4
InvokerHelper.runScript(Main, args) 5
}
}
1.Main
类继承了groovy.lang.Script
类
2.groovy.lang.Script
需要一个run
方法来返回值
3.脚本内容被放进run
方法里
4.Main
方法是自动产生的
5.代理执行脚本的run
方法
如果脚本在一个文件中,这样文件的名字被用来决定生成脚本类的名字,在这个例子中,如果文件名是Main.groovy
,脚本名字就会是Main。
方法
在脚本中定义方法也是可行的,如下:
int fib(int n) {
n < 2 ? 1 : fib(n-1) + fib(n-2)
}
assert fib(10)==89
你也可以混合方法和代码,生成的脚本类将会携带所有的方法,而且会组建所有的脚本内容到run
方法中。
println 'Hello' 1
int power(int n) { 2**n } 2
println "2^6==${power(6)}" 3
1.脚本开始
2.在脚本内容中定义方法
3.脚本其余部分
这段代码被转化成:
import org.codehaus.groovy.runtime.InvokerHelper
class Main extends Script {
int power(int n) { 2** n} 1
def run() {
println 'Hello' 2
println "2^6==${power(6)}" 3
}
static void main(String[] args) {
InvokerHelper.runScript(Main, args)
}
}
1.power
方法将会被拷贝进生成的脚本类中
2.第一个声明被拷贝进run
中
3.第二个声明也被拷贝进run
中
虽然Groovy从脚本帮你创建了脚本类,但是对用户来说是透明的,特别是,代码被编译成字节码,行数是被保持的。这是为了当有一个异常抛出的时候,堆栈会显示出行号对应的原始代码,而不是生成的代码。
变量
在脚本中的类型不需要类型定义,意思就是说像下面代码:
int x = 1
int y = 2
assert x+y == 3
我们可以像下面这么做:
x = 1
y = 2
assert x+y == 3
然而这两者的语义上面还有所不同:
1.像第一个例子中的变量定义,他是一个局部变量,他将会被编译器声明在run
方法中,脚本main主体是不可见的,特别地,这类变量不会被其他方法可见。
2.如果变量未声明,那么他就会变成脚本绑定,这个变量在方法间是可见的,尤为重要的是你需要用一个脚本跟应用打交道或者需要在脚本与应用间共享数据的时候。读者可以通过 integration guide来获取更多信息.
如果你不想通过绑定来使变量成为类的全局变量的话,那么你可以使用@Field annotation
(变量注解)。