Groovy 脚本基础全攻略,重磅
Groovy 支持各种类型的整型和数值类型,通常支持 Java 支持的那些,下面我们仔细来说说。
2-5-1 整型
Groovy 像 Java 一样支持如下一些整型,byte、char、short、int、long、java.lang.BigInteger。我们在使用中可以像下面例子一样:
// primitive types
byte b = 1
char c = 2
short s = 3
int i = 4
long l = 5
// infinite precision
BigInteger bi = 6
int xInt = 077
assert xInt == 63
int xInt = 0x77
assert xInt == 119
int xInt = 0b10101111
assert xInt == 175
2-5-2 浮点型
Groovy 像 Java 一样支持如下一些浮点型,float、double、java.lang.BigDecimal。我们在使用中可以像下面例子一样:
// primitive types
float f = 1.234
double d = 2.345
// infinite precision
BigDecimal bd = 3.456
assert 1e3 == 1_000.0
assert 2E4 == 20_000.0
assert 3e+1 == 30.0
assert 4E-2 == 0.04
2-6 Booleans 类型
Boolean 类型没啥解释的,和其他语言一样,就两个值,如下:
def myBooleanVariable = true
boolean untypedBooleanVar = false
booleanField = true
比较简单,没啥特例,自行脑补。
2-7 Lists 类型
Groovy 同样支持 java.util.List 类型,在 Groovy 中同样允许向列表中增加或者删除对象,允许在运行时改变列表的大小,保存在列表中的对象不受类型的限制;此外还可以通过超出列表范围的数来索引列表。如下例子:
//使用动态 List
def numbers = [1, 2, 3]
assert numbers instanceof List
assert numbers.size() == 3
//List 中存储任意类型
def heterogeneous = [1, "a", true]
//判断 List 默认类型
def arrayList = [1, 2, 3]
assert arrayList instanceof java.util.ArrayList
//使用 as 强转类型
def linkedList = [2, 3, 4] as LinkedList
assert linkedList instanceof java.util.LinkedList
//定义指定类型 List
LinkedList otherLinked = [3, 4, 5]
assert otherLinked instanceof java.util.LinkedList
//定义 List 使用
def letters = ['a', 'b', 'c', 'd']
//判断 item 值
assert letters[0] == 'a'
assert letters[1] == 'b'
//负数下标则从右向左 index
assert letters[-1] == 'd'
assert letters[-2] == 'c'
//指定 item 赋值判断
letters[2] = 'C'
assert letters[2] == 'C'
//给 List 追加 item
letters << 'e'
assert letters[ 4] == 'e'
assert letters[-1] == 'e'
//获取一段 List 子集
assert letters[1, 3] == ['b', 'd']
assert letters[2..4] == ['C', 'd', 'e']
//多维 List 支持
def multi = [[0, 1], [2, 3]]
assert multi[1][0] == 2
2-8 Arrays 类型
Groovy 中数组和 Java 类似,具体如下:
//定义初始化 String 数组
String[] arrStr = ['Ananas', 'Banana', 'Kiwi']
assert arrStr instanceof String[]
assert !(arrStr instanceof List)
//使用 def 定义初始化 int 数组
def numArr = [1, 2, 3] as int[]
assert numArr instanceof int[]
assert numArr.size() == 3
//声明定义多维数组指明宽度
def matrix3 = new Integer[3][3]
assert matrix3.size() == 3
//声明多维数组不指定宽度
Integer[][] matrix2
matrix2 = [[1, 2], [3, 4]]
assert matrix2 instanceof Integer[][]
//数组的元素使用及赋值操作
String[] names = ['Cédric', 'Guillaume', 'Jochen', 'Paul']
assert names[0] == 'Cédric'
names[2] = 'Blackdrag'
assert names[2] == 'Blackdrag'
2-9 Maps 类型
Map 是“键-值”对的集合,在 Groovy 中键 key 不一定是 String,可以是任何对象(实际上 Groovy 中的 Map 就是 java.util.Linke dHashMap)。如下:
//定义一个 Map
def colors = [red: '#FF0000', green: '#00FF00', blue: '#0000FF']
//获取一些指定 key 的 value 进行判断操作
assert colors['red'] == '#FF0000'
assert colors.green == '#00FF00'
//给指定 key 的对赋值 value 操作与判断
colors['pink'] = '#FF00FF'
colors.yellow = '#FFFF00'
assert colors.pink == '#FF00FF'
assert colors['yellow'] == '#FFFF00'
//判断 Map 的类型
assert colors instanceof java.util.LinkedHashMap
//访问 Map 中不存在的 key 为 null
assert colors.unknown == null
//定义 key 类型为数字的 Map
def numbers = [1: 'one', 2: 'two']
assert numbers[1] == 'one'
对于 Map 需要特别注意一种情况,如下:
//把一个定义的变量作为 Map 的 key,访问 Map 的该 key 是失败的
def key = 'name'
def person = [key: 'Guillaume']
assert !person.containsKey('name')
assert person.containsKey('key')
//把一个定义的变量作为 Map 的 key 的正确写法---添加括弧,访问 Map 的该 key 是成功的
person = [(key): 'Guillaume']
assert person.containsKey('name')
assert !person.containsKey('key')
**【工匠若水 [http://blog.csdn.net/yanbober](
) 转载请注明出处。[点我开始 Android 技术交流](
)】**
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,
new Car(make: 'Renault', model: 'Clio')]
assert cars*.make == ['Peugeot', null, 'Renault']
assert null*.make == null
关于 Groovy 的其他运算符就不多说,类比 Java 吧。
**【工匠若水 [http://blog.csdn.net/yanbober](
) 转载请注明出处。[点我开始 Android 技术交流](
)】**
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
}
//单个 Groovy 源码文件,运行会报错找不到 num 变量
int num = 1
def printNum(){
println num
}
//单个 Groovy 源码文件,运行 OK 成功
num = 1
def printNum(){
println num
}
上面的例子可以发现,我们如果想要在 Groovy 的方法中使用 Groovy 的变量则不能有修饰符。然而,如果我们想在 B.groovy 文件访问 A.groovy 文件的 num 变量咋办呢,我们可以使用 Field 注解,具体操作如下:
import groovy.transform.Field;
@Field num = 1
哈哈,这就是 Groovy 的变量作用域了,如果你想知道上面这些写法为啥出错,很简单,自己动手整成 Java 源码相信你一定可以看懂为啥鸟。
**【工匠若水 [http://blog.csdn.net/yanbober](
) 转载请注明出处。[点我开始 Android 技术交流](
)】**
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 {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 语言的函数指针非常像,我们定义好闭包后调用的方法有如下两种形式:
闭包对象.call(参数)
闭包对象(参数)
如下给出例子:
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,2) == 3
def closureWithTwoArgsAndOptionalTypes = { a, int b -> a+b }
assert closureWithTwoArgsAndOptionalTypes(1,2) == 3
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 闭包省略调运
很多方法的最后一个参数都是一个闭包,我们可以在这样的方法调运时进行略写括弧。比如:
def debugClosure(int num, String str, Closure closure){
//dosomething
}
debugClosure(1, "groovy", {
println"hello groovy!"
})
可以看见,当闭包作为闭包或方法的最后一个参数时我们可以将闭包从参数圆括号中提取出来接在最后,如果闭包是唯一的一个参数,则闭包或方法参数所在的圆括号也可以省略;对于有多个闭包参数的,只要是在参数声明最后的,均可以按上述方式省略。
**【工匠若水 [http://blog.csdn.net/yanbober](
) 转载请注明出处。[点我开始 Android 技术交流](
)】**
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 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 闭包操作,此方式不需要手动关闭流
new File(baseDir,'haiku.txt').withInputStream { stream ->
// do something ...
}
上面介绍了一些常用的文件读操作,其它的具体参见 API 和 GDK 吧。
写文件操作:
有了上面的读操作,接下来直接看几个写操作的例子得了,如下:
//向一个文件以 utf-8 编码写三行文字
new File(baseDir,'haiku.txt').withWriter('utf-8') { writer ->
writer.writeLine 'Into the ancient pond'
writer.writeLine 'A frog jumps'
writer.writeLine 'Water’s sound!'
}
评论