(点击上方公众号,可快速关注)
来源:工匠若水(@工匠若水)
链接:http://blog.csdn.net/yanbober/article/details/49047515
3 运算符
关于Groovy的运算符介绍类似于上面一样,我们重点突出与Java的不同点,相同点自行脑补。
Groovy支持**次方运算符,如下:
assert 2 ** 3 == 8
def f = 3
f **= 2
assert f == 9
Groovy非运算符如下:
assert (!true) == false
assert (!'foo') == false
assert (!'') == true
Groovy支持?.安全占位符,这个运算符主要用于避免空指针异常,譬如:
def person = Person.find { it.id == 123 }
def name = person?.name
assert name == null
Groovy支持.@直接域访问操作符,因为Groovy自动支持属性getter方法,但有时候我们有一个自己写的特殊getter方法,当不想调用这个特殊的getter方法则可以用直接域访问操作符。如下:
class User {
public final String name
User(String name) { this.name = name}
String getName() { "Name: $name" }
}
def user = new User('Bob')
assert user.name == 'Name: Bob'
assert user.@name == 'Bob'
Groovy支持.&方法指针操作符,因为闭包可以被作为一个方法的参数,如果想让一个方法作为另一个方法的参数则可以将一个方法当成一个闭包作为另一个方法的参数。如下:
def list = ['a','b','c']
//常规写法
list.each{
println it
}
String printName(name){
println name
//方法指针操作符写法
list.each(this.&printName)
Groovy支持将?:三目运算符简化为二目,如下:
displayName = user.name ? user.name : 'Anonymous'
displayName = user.name ?: 'Anonymous'
Groovy支持*.展开运算符,一个集合使用展开运算符可以得到一个元素为原集合各个元素执行后面指定方法所得值的集合,如下:
cars = [
new Car(make: 'Peugeot',model: '508'),
null,0);"> new Car(make: 'Renault',model: 'Clio')]
assert cars*.make == ['Peugeot',null,'Renault']
assert null*.make == null
关于Groovy的其他运算符就不多说,类比Java吧。
4 程序结构
这里主要讨论Groovy的代码组成结构,具体如下细则。
4-1 包名
包名的定义和作用及含义完全和Java一样,不再介绍,如下:
// defining a package named com.yoursite
package com.yoursite
4-2 Imports引入
常规的imports导包操作和Java一样,如下:
//例1:
import groovy.xml.MarkupBuilder
// using the imported class to create an object
def xml = new MarkupBuilder()
assert xml != null
//例2:
import groovy.xml.*
def markupBuilder = new MarkupBuilder()
assert markupBuilder != null
assert new StreamingMarkupBuilder() != null
//例3:
import static Boolean.FALSE
assert !FALSE
//例4:特殊的,相当于用as取别名
import static Calendar.getInstance as now
assert now().class == Calendar.getInstance().class
不过要特别注意,Groovy与Java类似,已经帮我们默认导入了一些常用的包,所以在我们使用这些包的类时就不用再像上面那样导入了,如下是自动导入的包列表:
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
4-3 脚本与类(脚本的实质)
相对于传统的Java类,一个包含main方法的Groovy类可以如下书写:
class Main {
static void main(String... args) {
println 'Groovy world!'
}
}
和Java一样,程序会从这个类的main方法开始执行,这是Groovy代码的一种写法,实际上执行Groovy代码完全可以不需要类或main方法,所以更简单的写法如下:
println 'Groovy world!'
上面这两中写法其实是一样的,具体我们可以通过如下命令进行编译为class文件:
groovyc demo.groovy //编译Groovy源码为class
我们使用反编译工具可以查看到这个demo.groovy类源码如下:
import org.codehaus.groovy.runtime.InvokerHelper
class Main extends Script {
def run() {
println 'Groovy world!'
}
static void main(String[] args) {
InvokerHelper.runScript(Main,args)
}
}
可以看见,上面我们写的groovy文件编译后的class其实是Java类,该类从Script类派生而来(查阅API);可以发现,每个脚本都会生成一个static main方法,我们执行groovy脚本的实质其实是执行的这个Java类的main方法,脚本源码里所有代码都被放到了run方法中,脚本中定义的方法(该例暂无)都会被定义在Main类中。
通过上面可以发现,Groovy的实质就是Java的class,也就是说他一定会和Java一样存在变量作用域!对哦,前面我们解释变量时竟然没说到这个东东,这里说下吧。看下面例子:
//单个Groovy源码文件,运行会报错找不到num变量
def num = 1
def printNum(){
println num
int num = 1
}
//单个Groovy源码文件,运行OK成功
num = 1
}
上面的例子可以发现,我们如果想要在Groovy的方法中使用Groovy的变量则不能有修饰符。然而,如果我们想在B.groovy文件访问A.groovy文件的num变量咋办呢,我们可以使用Field注解,具体操作如下:
import groovy.transform.Field;
@Field num = 1
哈哈,这就是Groovy的变量作用域了,如果你想知道上面这些写法为啥出错,很简单,自己动手整成Java源码相信你一定可以看懂为啥鸟。
5 闭包
Groovy的闭包(closure)是一个非常重要的概念,闭包是可以用作方法参数的代码块,Groovy的闭包更象是一个代码块或者方法指针,代码在某处被定义然后在其后的调用处执行。
5-1 语法
定义一个闭包:
{ [closureParameters -> ] statements }
//[closureparameters -> ]是可选的逗号分隔的参数列表,参数类似于方法的参数列表,这些参数可以是类型化或非类型化的。
如下给出几个有效的闭包定义例子:
//最基本的闭包
{ item++ }
//使用->将参数与代码分离
{ -> item++ }
//使用隐含参数it(后面有介绍)
{ println it }
//使用明确的参数it替代
{ it -> println it }
//使用显示的名为参数
{ name -> println name }
//接受两个参数的闭包
{ String x,int y ->
println "hey ${x} the value is ${y}"
//包含一个参数多个语句的闭包
{ reader ->
def line = reader.readLine()
line.trim()
}
闭包对象:
一个闭包其实就是一个groovy.lang.Closure类型的实例,如下:
//定义一个Closure类型的闭包
def listener = { e -> println "Clicked on $e.source" }
assert listener instanceof Closure
//定义直接指定为Closure类型的闭包
Closure callback = { println 'Done!' }
Closure<Boolean> isTextFile = {
File it -> it.name.endsWith('.txt')
调运闭包:
其实闭包和C语言的函数指针非常像,我们定义好闭包后调用的方法有如下两种形式:
如下给出例子:
def code = { 123 }
assert code() == 123
assert code.call() == 123
def isOdd = { int i-> i%2 == 1 }
assert isOdd(3) == true
assert isOdd.call(2) == false
特别注意,如果闭包没定义参数则默认隐含一个名为it的参数,如下例子:
def isEven = { it%2 == 0 }
assert isEven(3) == false
assert isEven.call(2) == true
5-2 参数
普通参数:
一个闭包的普通参数定义必须遵循如下一些原则:
参数类型可选
参数名字
可选的参数默认值
参数必须用逗号分隔
如下是一些例子:
def closureWithOneArg = { str -> str.toUpperCase() }
assert closureWithOneArg('groovy') == 'GROOVY'
def closureWithOneArgAndExplicitType = { String str -> str.toUpperCase() }
assert closureWithOneArgAndExplicitType('groovy') == 'GROOVY'
def closureWithTwoArgs = { a,b -> a+b }
assert closureWithTwoArgs(1,2) == 3
def closureWithTwoArgsAndExplicitTypes = { int a,int b -> a+b }
assert closureWithTwoArgsAndExplicitTypes(1,89);">def closureWithTwoArgsAndOptionalTypes = { a,89);">assert closureWithTwoArgsAndOptionalTypes(1,89);">def closureWithTwoArgAndDefaultValue = { int a,int b=2 -> a+b }
assert closureWithTwoArgAndDefaultValue(1) == 3
隐含参数:
当一个闭包没有显式定义一个参数列表时,闭包总是有一个隐式的it参数。如下:
def greeting = { "Hello,$it!" }
assert greeting('Patrick') == 'Hello,Patrick!'
上面的类似下面这个例子:
def greeting = { it -> "Hello,$it!" }
assert greeting('Patrick') == 'Hello,Patrick!'
当然啦,如果你想声明一个不接受任何参数的闭包,且必须限定为没有参数的调用,那么你必须将它声明为一个空的参数列表,如下:
def magicNumber = { -> 42 }
// this call will fail because the closure doesn't accept any argument
magicNumber(11)
可变长参数:
Groovy的闭包支持最后一个参数为不定长可变长度的参数,具体用法如下:
def concat1 = { String... args -> args.join('') }
assert concat1('abc','def') == 'abcdef'
def concat2 = { String[] args -> args.join('') }
assert concat2('abc','def') == 'abcdef'
def multiConcat = { int n,String... args ->
args.join('')*n
assert multiConcat(2,'abc','def') == 'abcdefabcdef'
5-3 闭包省略调运
很多方法的最后一个参数都是一个闭包,我们可以在这样的方法调运时进行略写括弧。比如:
6 GDK(Groovy Development Kit)
Groovy除了可以直接使用Java的JDK以外还有自己的一套GDK,其实也就是对JDK的一些类的二次封装罢了;一样,这是GDK官方API文档,写代码中请自行查阅。
6-1 I/O操作
Groovy提供了很多IO操作的方法,你可以使用Java的那写IO方法,但是没有Groovy的GDK提供的简单牛逼。
读文件操作:
我们先来看一个例子:
//读文件打印脚本
new File('/home/temp','haiku.txt').eachLine { line ->
println line
//读文件打印及打印行号脚本
new File(baseDir,'haiku.txt').eachLine { line,nb ->
println "Line $nb: $line"
}
可以看见,这是一个读文件打印每行的脚本,eachLine方法是GDK中File的方法,eachLine的参数是一个闭包,这里采用了简写省略括弧。
当然了,有时候你可能更加喜欢用Reader来操作,使用Reader时即使抛出异常也会自动关闭IO。如下:
def count = 0,MAXSIZE = 3
new File(baseDir,"haiku.txt").withReader { reader ->
while (reader.readLine()) {
if (++count > MAXSIZE) {
throw new RuntimeException('Haiku should only have 3 verses')
}
}
接着我们再看几个关于读文件的操作使用,如下:
//把读到的文件行内容全部存入List列表中
def list = new File(baseDir,'haiku.txt').collect {it}
//把读到的文件行内容全部存入String数组列表中
def array = new File(baseDir,'haiku.txt') as String[]
//把读到的文件内容全部转存为byte数组
byte[] contents = file.bytes
//把读到的文件转为InputStream,切记此方式需要手动关闭流
def is = new File(baseDir,'haiku.txt').newInputStream()
// do something ...
is.close()
//把读到的文件以InputStream闭包操作,此方式不需要手动关闭流
// do something ...
}
上面介绍了一些常用的文件读操作,其它的具体参见API和GDK吧。
写文件操作:
有了上面的读操作,接下来直接看几个写操作的例子得了,如下:
//向一个文件以utf-8编码写三行文字
writer.writeLine 'Into the ancient pond'
writer.writeLine 'A frog jumps'
writer.writeLine 'Water’s sound!'
//上面的写法可以直接替换为此写法
A frog jumps
Water’s sound!'''
//直接以byte数组形式写入文件
file.bytes = [66,22,11]
//类似上面读操作,可以使用OutputStream进行输出流操作,记得手动关闭
def os = new File(baseDir,'data.bin').newOutputStream()
os.close()
//类似上面读操作,可以使用OutputStream闭包进行输出流操作,不用手动关闭
}
上面介绍了一些常用的文件写操作,其它的具体参见API和GDK吧。
文件树操作:
在脚本环境中,遍历一个文件树是很常见的需求,Groovy提供了多种方法来满足这个需求。如下:
//遍历所有指定路径下文件名打印
dir.eachFile { file ->
println file.name
//遍历所有指定路径下符合正则匹配的文件名打印
dir.eachFileMatch(~/.*.txt/) { file ->
//深度遍历打印名字
dir.eachFileRecurse { file ->
//深度遍历打印名字,只包含文件类型
dir.eachFileRecurse(FileType.FILES) { file ->
//允许设置特殊标记规则的遍历操作
dir.traverse { file ->
if (file.directory && file.name=='bin') {
FileVisitResult.TERMINATE
} else {
FileVisitResult.CONTINUE
执行外部程序:
Groovy提供一种简单方式来处理执行外部命令行后的输出流操作。如下:
def process = "ls -l".execute()
println "Found text ${process.text}"
execute方法返回一个java.lang.Process对象,支持in、out、err的信息反馈。在看一个例子,如下:
process.in.eachLine { line ->
println line
}
上面使用闭包操作打印出执行命令行的输入流信息。
6-2 有用的工具类操作
ConfigSlurper配置:
ConfigSlurper是一个配置管理文件读取工具类,类似于Java的*.properties文件,如下:
def config = new ConfigSlurper().parse('''
app.date = new Date()
app.age = 42
app {
name = "Test${42}"
''')
assert config.app.date instanceof Date
assert config.app.age == 42
assert config.app.name == 'Test42'
上面介绍了一些常用的属性配置操作,其它的具体参见API和GDK吧。
Expando扩展:
def expando = new Expando()
expando.toString = { -> 'John' }
expando.say = { String s -> "John says: ${s}" }
assert expando as String == 'John'
assert expando.say('Hi') == 'John says: Hi'
上面介绍了一些常用的拓展操作,其它的具体参见API和GDK吧。
6-2 其他操作
还有很多其他操作,这里就不一一列举,详情参考官方文档即可,譬如JSON处理、XML解析啥玩意的,自行需求摸索吧。
7 DSL(Domain Specific Languages)领域相关语言
这个就不特殊说明了,只在这里提一下,因为我们前边很多地方已经用过它了,加上我们只是干货基础掌握,所以不做深入探讨。
DSL是一种特定领域的语言(功能领域、业务领域),Groovy是通用的编程语言,所以不是DSL,但是Groovy却对编写全新的DSL提供了很好的支持,这些支持来自于Groovy自身语法的特性,如下:
所以说这个基础入门没必要特别深入理解,简单的前面都用过了,理解DSL作用即可,点到为止,详情参考官方文档。
8 Groovy脚本基础总结
其实没啥总结的,Groovy其实可以当做Java来看待,只是它提供的支持比Java还好而已,在学习Groovy是一定要和Java进行对比学习,这样才能速成基础。
安卓应用频道
微信号:AndroidPD

打造东半球最好的 安卓技术 微信号
--------------------------------------
商务合作QQ:2302462408
投稿网址:top.jobbole.com
