基础语法
数据类型
和Java一样,有8种基础的内置类型
- byte -表示字节值
- short -表示一个短整型
- int -表示整数
- long -表示一个长整型
- float -表示32位浮点数
- double -表示64位浮点数
- char -单个字符
- boolean -表示一个布尔值,可以是true或false
注意,Groovy中会将这些基础类型转换成对象类型,也就是Java中的包装类,因此Groovy没有基本数据类型
int n = 0;
boolean b = false;
// 打印真实类型
println(n.class);
println(b.class);
输出:
class java.lang.Integer
class java.lang.Boolean
变量声明
有三种方式声明变量
// 1. 声明静态类型变量
int x = 5;
int y = 6;
// 打印
println(x);
println(X);
// 2. 声明动态类型变量
def name = "Joe";
println(name);
name = 2017;
println(name)
// 3. 省略def关键字,等同动态类型变量
age = 20;
println(age);
age = "18";
println(age)
字符串
Groovy中提供了两种字符串类型,一种是对应Java中的字符串的String类型,另一种是可以使用插值表达式的GString类型。
共有四种方式声明字符串字面量
// 1. 单引号声明
def s1 = 'zhangsan';
// 2. 双引号声明
def s2 = "lisi";
// 3. 三引号声明
def s3 = """
line1 break
line2
""";
// 4. 正斜杠声明
def s4 = /hello/;
println(s1.class);
println(s2.class);
println(s3.class);
println(s4.class);
打印:
class java.lang.String
class java.lang.String
class java.lang.String
class java.lang.String
这四种声明方式的区别:
- 单引号声明的字符串不能包含插值表达式
- 双引号字符串可以包含插值表达式
- 正斜杠声明基本等同双引号声明,可包含插值表达式,且可以支持多行
- 三引号声明的字符串可以包含原始格式,例如换行符等,因此用于声明多行字符串
需要注意,Groovy中的GString类型并非主动声明,而是由编译器智能判断,当检测到包含插值表达式时,自动将String转为GString类型。
def str = "haha";
def s1 = 'zhangsan $str';
// 插值表达式。当包含一个表达式时,可以使用${},单个变量使用$即可
def s2 = "lisi $str";
println(s1)
println(s1.class);
println(s2);
println(s2.class);
打印:
zhangsan $str
class java.lang.String
lisi haha
class org.codehaus.groovy.runtime.GStringImpl
Groovy没有明确的字符类型(Characters)。但有三种方式来将字符串作为字符处理
char c1 = 'A'
println(c1 instanceof Character)
def c2 = 'B' as char
println(c2 instanceof Character)
def c3 = (char)'C'
println(c3 instanceof Character)
集合类
List
Groovy同样支持java.util.List类型
// 声明 List
def numbers = [1, 2, 3, 4, 5];
ArrayList arrayList = [1, "a", true];
// 使用索引访问
println(arrayList[0] == 1);
println(arrayList[1] == "a");
// 支持负数索引。负值表示从右向左index,-1为列表末尾的元素
println(arrayList[-1]);
// 支持切片,截取包含部分元素的子集(注意,切片操作亦适用于字符串)
// 1..3 表示截取索引值为 1<= index <3 的子集
println(numbers[1..3]);
// 添加元素
numbers.add(6);
// groovy提供的一种简便的添加操作
numbers << 7;
// 删除指定位置的元素
arrayList.remove(0);
// 新的列表迭代方式
numbers.each {println(it)};
注意:
- 支持Java中List类的一些方法,同时Groovy还提供了一些新的语法
- 使用切片时需注意,切片返回的是对原列表的引用而不是拷贝,这意味着修改切片将改变原列表
Map
Groovy中的Map是java.util.LinkedHashMap
// 声明一个Map
def colors = [red: '#FF0000', green: '#00FF00', blue: '#0000FF'];
// 两种访问数据的方式
println(colors["red"]);
println(colors."blue");
// 添加数据
colors['pink'] = '#FF00FF';
// 删除数据
colors.remove("green");
def name = 'zhangsan'
// 不能直接将变量作为键存放
def person = [name: 18]
println(person.containsKey(name)) // false
// 使用括号包裹键
def person2 = [(name): 18]
println(person2.containsKey(name)) // true
// Map的迭代
colors.each {entry->println("Key=${entry.key} Value=${entry.value}")}
函数与闭包
Groovy中既可以像Java一样面向对象编程,也可以像Python一样脚本化的面向过程编程。因此我们可以不需要main函数,,也不必声明一个类,可以直接在脚本中定义顶级函数,并从上往下执行代码。
// 两种定义函数的方式
// 1. 使用def关键字定义,不需声明返回值类型
def function1(int a,int b){
println("$a,$b");
}
// 2. 不使用def,但要显示声明返回值类型
String function2(a,b){
return "$a $b";
}
function1();
Groovy中还需要注意:
- 函数的形参可以省略类型声明
- 函数的形参可以指定默认值
- 函数还可以声明不定参
- 函数可以省略
return语句,默认最后一行表达式的值为返回值 - 在最外层的函数调用中可以省略圆括号“()”
- Groovy中可以省略语句结尾的分号,且在大部分脚本中都会省略
// 声明一个加法函数,并给形参设置默认值
def add(x=0,y=10){
// 等价于 return x+y;
x+y
}
// 调用add函数,省略圆括号以及分号(无参函数不能省略圆括号)
add 8
add 7,9
// 最外层的println函数可以省略圆括号
// 等价于 println(add(7,8));
println add(7,8)
// println add 7,8 错误!此时add不能省略圆括号,不是最外层调用
// 声明一个不定参的函数
def sayHi(String... args){
// 使用传统for循环遍历不定参
for (int i = 0;i < args.size(); i++) {
println("Hi,${args[i]} !")
}
}
// 传入不定参
sayHi "Alice","Bob","Alex","Bruce","Tom"
闭包
闭包是Groovy语言的重中之重,一定要熟练掌握。根据编程语言的一般通用概念,闭包通常指匿名函数和Lamda表达式,然而Groovy中对闭包的定义则更宽松,一般指花括号括起来的一段代码块。Groovy中闭包的写法也较为随意和宽松,对初学者理解阅读起来容易产生一定困扰。
闭包的一般格式:
{ [形参 -> ] 语句 }
这里使用"[]"包裹的部分是表示可选的,不是必须的,通常情况下都会省略。它相当于函数的形参列表,使用->符号将形参与闭包的语句间隔起来
// 使用静态类型声明一个闭包变量
Closure callback = { param -> println "param=$param" }
// 调用闭包的两种方式
// 1. 通过call方法调用
callback.call("name")
// 2. 直接调用
callback(88)
// 简化形式声明一个闭包,通常会省略参数和“->”符
def func = {println "param=$it"}
func("hello")
需要注意,闭包仅包含一个参数时,可以使用上述的简化形式声明。当一个闭包没有显式定义一个参数列表时,闭包总是有一个隐式的it参数,上例中等价于{it -> println "param=$it"}
如果你想声明一个不接受任何参数的闭包,且限定为不接受参数的调用,那么就必须显式声明一个空的参数列表,这样闭包内就不会包含隐式参数it
def hello = { -> println "hello" }
闭包作为函数参数
// 将闭包作为函数形参
def calc(a,b, closure){
closure(a,b)
}
// 传入一个做加法的闭包
calc(8,10,{
a,b-> println a + b
})
使用闭包时需要注意:
- 当函数有多个参数时,通常将闭包定义到最后一个参数的位置
- 闭包的形参也支持不定参
- 闭包通常有一种怪异的省略调用写法,在DSL中是一种主要用法,必须熟悉
以下省略调用,等同于上例中的calc调用
// 闭包的省略调用
// 当闭包位于函数最后一个参数时,可以将闭包提取到函数圆括号的外面进行调用
// 这种调用方式,好似将闭包挂载到函数上进行调用
calc (8,10) {
a,b-> println a + b
}
前面讲了,当函数调用处于最外层时,还可以省略圆括号,因此还有以下写法
calc 8,10,{
a,b-> println a + b
}
// 等价于以下形式去除括号
calc(8,10,{
a,b-> println a + b
})
以下写法需特别注意,极易与函数声明混淆
// 声明只有一个闭包做形参的函数
def greet(closure){
closure()
}
// 闭包省略调用
greet(){println 'hello!'}
// 省略括号调用闭包
greet{println 'hello!'}
关于Groovy的API的具体指南,可查看Groovy API文档
公众号“编程之路从0到1”