【Groovy语法规则】
下面是Groovy的一个简单的代码示例:
public class ProjectVersion {
private int major; //大版本
private int minor; //小版本
public ProjectVersion(int major, int minor) {
this.major = major;
this.minor = minor // Groovy分号是可选的
}
public int getMajor() {
major // Groovy最后一个表达式的值会作为返回值
}
public void setMajor(int major) {
this.major = major;
}
// Groovy自动为属性添加getter和setter方法
}
ProjectVersion v1 = new ProjectVersion(1,1);
v1.minor = 2 // 直接使用.获取对象
println v1.minor
ProjectVersion v2 = null;
println v2 == v2; //不会抛出空指针异常,因为Groovy中==和equals()方法一样
我们看到,GroovyBeanExample只定义了一个私有的minor属性,并没有getter和setter。但是在使用时,我们可以直接对minor进行访问,无论时读还是写。事实上,我们并不是在直接访问minor进属性,当我们执行"v1.minor = 2"时,我们实际调用的是"v1.setMinor(2)",而在调用"println v1.minor"时,我们实际调用的是"println v1.getMinor()"。
这里的原因在于,Groovy动态地为minor创建了getter和setter,采用像直接访问的方式的目的是为了增加代码的可读性,使它更加自然,而在内部,Groovy依然是在调用setter和getter方法。
【高效的Groovy特性】
1. 可选的类型定义
def version = 1
def类似于JavaScript中的var,不是一个变量类型,它定义一个变量,但是这个变量的类型是自动推出来的。def可省略。
2. assert
assert version = 2
3. 括号是可选的
Groovy中函数调用的时候还可以不加括号。比如:
println(version)
println version
4.字符串
字符串有三种形式:
// 单引号:仅仅是一个字符串
def s1 = 'gradle'
// 双引号:可以插入一个变量
def s2 = "gradle version is ${version}"
//三个单引号:可以换行
def s3 = '''my
name is
gradle'''
5. 集合API
(1)list
def buildTool = ['ant','maven']
buildTool << 'gradle' // 往list中追加元素
assert buildTool.getClass() == AarrayList
assert buildTool.size() == 3
(2)map
def buildYears = ['ant':2000,'maven':2004]
buildYears.gradle = 2009 // 往map中追加元素
// 获取map中元素有两种形式
println buildYears.ant
println buildYears['gradle']
6. 闭包
闭包就是一个代码块。闭包的定义格式:
{ [closureParameters -> ] statements }
闭包的参数列表是可选的,参数的类型也是可选的。如果我们不指定参数的类型,会由编译器自动推断。下面介绍在Groovy种常见的闭包使用方式。
创建闭包有三种方法:
//(1)使用 def 声明一个闭包
// 1.1 包含参数的闭包,箭头后面就是代码块
def c1 = {
v ->
print v
}
// 1.2 不包含参数的闭包
def c1 = {
print 'hello'
}
//(2)使用指定类型 Closure 声明一个闭包
Closure callback = { println 'Done!' }
//(3)使用泛型 声明一个固定返回类型的闭包
Closure<Boolean> isTextFile = {
File it -> it.name.endsWith('.txt') //返回boolean类型
}
使用闭包作为参数的方法:
def method1(Closure closure){
closure('prarm') // 执行带参数的闭包
}
def method2(Closure closure){
closure() // 执行不带参数的闭包
}
// 执行方法
method1(c1);
method2(c2);
下面给出一个groovy的文件进行讲解:
// 构建脚本中都是有个Projet实例的(项目实例)
apply plugin:'java' // apply是Project实例的一个方法,调用apply()方法和调用project.apply()方法的效果是一样的;Groovy中函数调用的时候可以不加括号;plugin参数的值等于java
version = '0.1' // version这个属性的值为0.1
repositories{ // repositories是一个方法;{mavenCentral()}是一个闭包
mavenCentral()
}
dependencies{
compile 'commons-codec:commons-codec:1.6'
}
【delegate机制】
Gradle大量地使用了Groovy闭包的delegate机制。简单来说,delegate机制可以使我们将一个闭包中的执行代码的作用对象设置成任意其他对象。比如:
class Child {
private String name
}
class Parent {
Child child = new Child();
void configChild(Closure c) {
c.delegate = child
c.setResolveStrategy Closure.DELEGATE_FIRST
c()
}
}
def parent = new Parent()
parent.configChild {
name = "child name"
}
println parent.child.name
在上面的例子中,当我们调用configChild()方法时,我们并没有指出name属性是属于Child的,但是它的确是在设置Child的name属性。事实上光从该方法的调用中,我们根本不知道name是属于哪个对象的,你可能会认为它是属于Parent的。真实情况是,在默认情况下,name的确被认为是属于Parent的,但是我们在configChild()方法的定义中做了手脚,使其不再访问Parent中的name(Parent也没有name属性),而是Child的name。在configChild()方法中,我们将该方法接受的闭包的delegate设置成了child,然后将该闭包的ResolveStrategy设置成了DELEGATE_FIRST。这样,在调用configChild()时,所跟闭包中代码被代理到了child上,即这些代码实际上是在child上执行的。
此外,闭包的ResolveStrategy在默认情况下是OWNER_FIRST,即它会先查找闭包的owner(这里即parent),如果owner存在,则在owner上执行闭包中的代码。这里我们将其设置成了DELEGATE_FIRST,即该闭包会首先查找delegate(本例中即child),如果找到,该闭包便会在delegate上执行。
你可能会发现,在使用Gradle时,我们并没有像上面的parent.configChild()一样指明方法调用的对象,而是在build.gradle文件中直接调用task(),apply()和configuration()方法等,这是因为在没有说明调用对象的情况下,Gradle会自动将调用对象设置成当前Project。比如调用apply()方法和调用project.apply()方法的效果是一样的。查查Gradle的Project文档,你会发现这些方法都是Project类的方法。