Groovy 语法
Groovy 基本语法
- 在 Groovy 中导入语句
用 import
语句来导入
1
2
| import groovy.xml.MarkupBuilder
def xml = new MarkupBuilder()
|
默认情况下,Groovy 在代码中导入了以下库,这些库不需要显示地导入它们:
1
2
3
4
5
6
7
8
9
10
| 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
|
- Groovy 令牌 (Token)
令牌可以是一个关键字、一个标识符、常量、字符串文字或符号
上面的代码中有,有两个令牌:关键字 println 和字符串 Hello World。
- Groovy 注释
单行注释 //
多行注释 /* */
文档注释 /** */
- 分号
像 Java 语言一样,需要分号在 Groovy 定义的多个语句之间进行区分。 - 标识符 (Identifiers)
标识符被用来定义变量、函数或其他用户定义的变量。
1
2
3
| def employeeName
def student
def stu_name
|
- 关键字 (Keywords)
关键字作为名称建议是在 Groovy 编程语言中保留的特殊字。 下表列出了在 Groovy 中定义的关键字。
- 空白 (Whitespaces)
空白是在编程语言如 Java 和 Groovy 用来形容空格,制表符,换行符和注释术语。空格分隔从另一个声明的一部分,使编译器,其中一个元素标识的声明。
例如,在下面的代码示例,存在关键字 def 和变量 x 之间的空白。这是为了让编译器知道 DEF 是需要被使用,并且是 x 应该是需要被定义的变量名的关键字。
- 文字 (Literals)
文字是在 groovy 中表示固定值的符号。Groovy 语言有符号整数,浮点数,字符和字符串。下面是一些在 Groovy 编程语言文字的例子:
- Groovy 变量
Groovy 中的变量可以通过两种方式定义:使用数据类型的本地语法,或者使用 def 关键字。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
| // 明确提供类型
int x = 5;
用def关键字
def _Name = "Joe";
//强类型定义
//int x=1
//double y=3.14
//char ch='a'
//boolean flag=true;
//弱类型定义
def x=1 //如果没有初始值,是不能直接使用的,会报NullPointerException
def y=3.14
def ch='a'
def flag=true
//println x.class
//println y.class
//println ch.class
//println flag.class
//弱类型是可以自动转型的
x='jett'
y="jett"
ch='''jett'''
println x.class
println y.class
println ch.class
|
Groovy 运算符
运算符是一个符号,通知编译器执行特定的数学或逻辑操作。
Groovy 中有以下类型的运算符:
算法运算符
Groovy 语言支持正常的算术运算符任何语言。以下是在 Groovy 中可用的算术运算符:
运算符 | 描述 | 例子 |
---|
+ | 两个操作数的加法 | 1 + 2 将得到 3 |
- | 第一第二操作数相减 | 2 - 1 将得到 1 |
* | 两个操作数的乘法 | 2 * 2 将得到 4 |
/ | 两个操作数的除法 | 3/2 将得到 1.5 |
% | 取模运算 | 3%2 将得到 1 |
++ | 自增运算,在自身值的基础上加 1 | INT X = 5;X ++;X 将得到 6 |
– | 自减运算,在自身值的基础上减 1 | INT X = 5;X - -;X 将得到 4 |
关系运算符
关系运算符允许对象的比较。以下是在 Groovy 中可用的关系运算符:
- ==
测试两个对象之间是否相等 - !=
测试两个对象之间是否不等 - «br >检查是否左边的对象是小于右边的对象
- <=
检查是否向左对象是小于或等于右边的对象
检查是否左边的对象比右边的对象大
逻辑运算符
逻辑运算符用于计算布尔表达式:
- && 逻辑 “ 与 “ 运算
- ! 逻辑 “ 非 “ 运算
位运算符
Groovy 中提供了四个位运算符。以下是在 Groovy 中可用的位运算符:
- &
按位 “ 与 “ 运算 - ^
按位 “ 异或 “ 运算 - ~
按位 “ 反 “ 运算
赋值运算符
Groovy 语言也提供了赋值操作符。以下是在 Groovy 提供的赋值运算符:
范围运算符
Groovy 支持范围的概念,并在..符号的帮助下提供范围运算符的符号:
这只是定义了一个简单的整数范围,存储到一个局部变量称为范围内的下限为 0 和上限为 5。
运算符优先级
Groovy 数据类型
内置数据类型
1
2
3
4
5
6
7
8
9
| byte 用来表示字节值
short 用来表示一个短整型
int 用来表示整数
long 用来表示一个长整型
float 用来表示32位浮点数
double 用来表示64位浮点数
char 定义了单个字符文字
Boolean 表示一个布尔值,可以是true和false
String 以字符串的形式表示的文本
|
数字类
类型除了基本类型,还允许以下对象类型(有时称为包装器类型)
1
2
3
4
5
6
| java.lang.Byte
java.lang.Short
java.lang.Integer
java.lang.Long
java.lang.Float
java.lang.Double
|
此外,以下类可用于支持高精度计算:
1
2
| java.math.BigInteger 不可变的任意精度的有符号整数数字
Java.math.BigDecimal 不可变的任意精度的有符号十进制数
|
Groovy 循环
循环 while/for/for-in
- while
while 语句首先通过计算条件表达式(布尔值)来执行,如果结果为真,则执行 while 循环中的语句。
1
2
3
4
5
| int count = 0
while (count < 5) {
println(count)
count++
}
|
1
2
3
| for (int i = 0; i < 5; i++) {
print(" " + i)
}
|
1
2
3
4
| int[] arr = [1, 2, 3, 4]
for (int i in arr) {
print(i + " ")
}
|
循环控制语句 break/continue
- break
break 语句用于改变循环和 switch 语句内的控制流。 - continue
continue 语句补充了 break 语句。它的使用仅限于 while 和 for 循环。
Groovy 条件语句
条件声明需要程序指定一个或者多个条件进行判断,如果条件被确定为真,则要执行一个或多个语句;如果条件被确定为假,则要执行其他语句。
- if 语句
这个语句的一般工作是首先在 if 语句中计算一个条件。如果条件为真,它然后执行语句。 - if/else 语句
这个语句的一般工作是首先在 if 语句中计算一个条件。如果条件为真,则其后执行语句,并在 else 条件之前停止并退出循环。如果条件为假,则执行 else 语句块中的语句,然后退出循环。 - 嵌套 if 语句
1
2
3
4
5
6
7
8
| int a = 97
if (a > 100) {
println("大于100")
} else if (a > 90) {
println("大于90")
} else {
println("小于90")
}
|
- switch 语句
同 Java 中的 switch 语句 - 嵌套 switch 语句
switch 中嵌套 switch 语句
Groovy 方法
Groovy 中的方法是使用返回类型或用 def 关键字定义的。方法可以接收任意的参数。定义参数时,不必显示定义类型。可以添加修饰符(public,private,protected)。默认情况下,如果没提供修饰符,默认为 public。
方法参数
1
2
3
| static def sum(int a, int b) {
return a + b
}
|
默认参数
Groovy 中还有一个规定来指定方法中的参数的默认值。 如果没有值传递给参数的方法,则使用缺省值。 如果使用非默认和默认参数,则必须注意,默认参数应在参数列表的末尾定义。
1
2
3
| def sum1(int a, int b = 5) {
return a + b
}
|
方法返回值
方法也可以将值返回到调用程序。 这在现在编程语言中是必需的,其中方法执行某种计算,然后将所需值返回到调用方法。
其他同 Java 语言特性
实例方法 (setter/getter)、本地和全局参数、方法属性 (this)、
Groovy 文件 I/O
Groovy 在使用 I/O 时提供了许多辅助方法。 Groovy 提供了更简单的类来为文件提供以下功能:
- 读取文件
- 写入文件
- 遍历文件树
- 读取和写入数据对象到文件
始终可以使用下面列出的用于文件 I / O 操作的标准 Java 类:
- java.io.File
- java.io.InputStream
- java.io.OutputStream
- java.io.Reader
- java.io.Writer
1、读取文件
以下示例将输出 Groovy 中的文本文件的所有行。方法 eachLine 内置在 Groovy 中的 File 类中,目的是确保文本文件的每一行都被读取。
1
2
3
4
5
6
7
| static void test1() {
def name = "C:/Users/zengfansheng/Desktop/example.txt"
File file = new File(name)
file.eachLine {
line -> println("line:$line")
}
}
|
File 类用于实例化以文件名作为参数的新对象。 然后它接受 eachLine 的函数,将它放到一个 line 的变量并相应地打印它。
2、读取文件的内容到字符串
如果要将文件的整个内容作为字符串获取,可以使用文件类的 text 属性:
1
2
| File file = new File("C:/Users/zengfansheng/Desktop/example.txt")
println file.text
|
3、写入文件
如果你想写入文件,你需要使用作家类输出文本到一个文件中。
1
2
3
4
| File file = new File("C:/Users/zengfansheng/Desktop/", "example2.txt")
file.withWriter("utf-8") {
writer -> writer.writeLine "hello world write file."
}
|
4、获取文件的大小
如果要获取文件的大小,可以使用文件类的 length 属性来获取文件的大小。
1
2
| File file = new File("C:/Users/zengfansheng/Desktop/", "example2.txt")
println("The file ${file.absolutePath} has ${file.length()} bytes")
|
5、测试文件是否是目录
1
2
3
| File file = new File("C:/Users/zengfansheng/Desktop/", "example2.txt")
println "File? ${file.isFile()}"
println "Directory? ${file.isDirectory()}"
|
6、创建目录
如果要创建一个新目录,可以使用 File 类的 mkdir 函数。
1
2
| File file = new File("C:/Users/zengfansheng/Desktop/test11")
file.mkdirs() // file.mkdir()
|
7、删除文件
8、复制文件
1
2
3
| File src = new File("C:/Users/zengfansheng/Desktop/", "src.txt")
File dst = new File("C:/Users/zengfansheng/Desktop/", "dst.txt")
dst << src.text
|
将创建文件 dst.txt,并将文件 src.txt 的所有内容复制到此文件。
9、获取目录内容
Groovy 还提供了列出驱动器中的驱动器和文件的功能。
以下示例显示如何使用 File 类的 listRoots()
函数显示机器上的所有驱动器:
1
2
3
4
5
| def rootFiles = new File("C:/Users/zengfansheng/Desktop/hexo")
File[] files = rootFiles.listRoots()
files.each {
file -> println file.absolutePath
}
|
以下示例显示如何使用 File 类的 eachFile()
函数列出特定目录中的文件(仅列出目录的子文目录和文件):
1
2
3
4
| def rootFiles = new File("C:/Users/zengfansheng/Desktop/andfix")
rootFiles.eachFile {
file -> println file.absolutePath
}
|
如果要递归显示目录及其子目录中的所有文件,则可以使用 File 类的 eachFileRecurse()
函数。以下示例显示如何完成此操作。
1
2
3
4
| def rootFiles = new File("C:/Users/zengfansheng/Desktop/andfix")
rootFiles.eachFileRecurse {
file -> println file.absolutePath
}
|
Groovy 字符串
Groovy 提供了多种表示 String 字面量的方法。 Groovy 中的字符串可以用 单引号(')
,双引号(")
或 三引号("')
括起来。此外,由三重引号括起来的 Groovy 字符串可以跨越多行。
1
2
3
4
| String a = 'this is a string'
String b = "this is b string"
String c = "' fajlfdjdlkasjfdljc" +
"fjaldjf '"
|
字符串索引
Groovy 中的字符串是字符的有序序列。字符串中的单个字符可以通过其位置访问。这由索引位置给出。
字符串索引从零开始,以小于字符串长度的一个结束。 Groovy 还允许负索引从字符串的末尾开始计数。
1
2
3
| String a = 'this is a string'
println(a[3]) // s 3表示第4个字符
println(a[-2]) // n , -2表示倒数第2个字符
|
字符串操作
1、+
字符串连接
字符串连接可以通过简单的 +
运算符来完成。
1
2
3
| String a = "Hello"
String b = "World"
println(a+b)
|
2、*
字符串重复
字符串的重复可以通过简单的 *
运算符完成。
- 参数:
- 一个字符串作为 * 操作符的左操作数
- 操作符右侧的数字,表示字符串需要重复的次数。
- 案例:
1
2
| String a = "Hello"
println(a*3)
|
3、length() 字符串的长度
字符串方法
除了 Java 中 String 的方法外,还有:
- String center(Number numberOfChars)
返回一个新的长度为 numberOfChars 的字符串,如果 numberOfChars 小于 String 本身长度,那么返回 String 本身;如果 numberOfChars 大于 String 本身长度,该字符串由左侧和右侧用空格字符达到 numberOfChars 长度字符串 - int compareToIgnoreCase(String str)
按字母顺序比较两个字符串,忽略大小写差异。返回一个负整数,零或正整数,因为指定的 String 大于,等于或小于此 String,忽略大小写注意事项。 - void eachMatch(String regex, Closure close)
处理每个正则表达式组匹配的给定 String 的子字符串
1
2
3
4
| String hello = "Hello World!"
hello.eachMatch(".") {
ch -> println(ch)
}
|
- String minus(Object) & String plus(Object)
minus() 返回删除字符串的值部分新的字符串。
plus() 返回原字符串和追加字符串组成的新的字符串
1
2
3
| String hello = "Hello World!"
println(hello.minus("Hell")) // o World!
println(hello.plus("hacket")) // Hello World!hacket
|
- String next() & String previous()
next() 此方法由 ++ 运算符为 String 类调用。它增加给定字符串中的最后一个字符。
previous() 和 next() 相反,最后一个字符 – 操作
1
2
3
| String hello = "Hello World"
println(hello.next()) // Hello Worle
println(hello.previous()) // Hello Worlc
|
- String padLeft(Number numberOfCharacters,String padding) & String padRight(Number numberOfCharacters,String padding)
padLeft() numberOfCharacters 小于 String 本身,返回 String 本身;如果大于 String 本身,在左边追加 numberOfCharacters-length() 个 padding
padRight() numberOfCharacters 小于 String 本身,返回 String 本身;如果大于 String 本身,在右边追加 numberOfCharacters-length() 个 padding
1
2
3
| String hello = "Hello World"
println(hello.padLeft(hello.length() + 4, "+++")) // ++++Hello World
println(hello.padRight(hello.length() + 1, "-")) // Hello World-
|
- String reverse()
创建一个与此 String 相反的新字符串。
1
2
| String hello = "Hello World"
println(hello.reverse()) // dlroW olleH
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
| //单引号 和java中是一样的
def name='jett'
//双引号
def name2="Hello:${name}"
//三引号 原始格式
def name3='''hello
jett "'''
//println name
//println name2
//println name3
//println name.class
//println name2.class
//println name3.class
//输入表达式
//def sum="${3+2}${name}"
//println sum
//
//String echo(String msg){
// println msg
//}
//echo(sum)
def string='hello'
def string2='el'
//groovy中常用的string相关的API
println string>string2
println string[1..2]
//减法
println string.minus(string2)
//逆序
println string.reverse()
//首字母大写
println string.capitalize()
//字符串中是否有数字字符
println string.isEmpty()
|
Groovy 范围 (ranges)
Range 是指定范围值的序列。Range 由序列中的第一个和最后一个值表示,Range 可以是 inclusive 或 exclusive。inclusive Range 包括从第一个到最后一个的所有值,exclusive Range 包括除最后一个之外的所有值。
- 1 .. 10 - 包含范围的示例
- 1 ..< 10 - 排除范围的示例
- ‘a’ .. ‘x’ - 范围也可以由字符组成
- 10 .. 1 - 范围也可以按降序排列
- ‘x’ .. ‘a’ - 范围也可以由字符组成并按降序排列。
Range 方法
- boolean contains(Object obj)
检查范围是否包含 obj。如果此范围包含指定的元素,则返回 true。 - Object get(int index)
返回此范围中指定位置 index 处的元素。 - Comparable getFrom()
获取此范围的较低值。不管是升序还是降序 - Comparable getTo()
获取此范围的上限值。不管是升序还是降序 - boolean isReverse()
这是一个反向的范围。是否范围反转的布尔值 true 或 false。(升序为 false,降序为 true) - int size()
返回此范围中的元素数。 - List subList(int fromIndex, int toIndex)
返回此指定的 fromIndex(包括)和 toIndex(排除)之间的此范围部分的视图。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
| static void test10() {
def v1 = 1..10
def v2 = 1..<10
def v3 = 'a'..'x'
def v4 = 10..1
def v5 = 'x'..'a'
println(v1)
println(v2)
println(v3)
println(v4)
println(v5)
println("================================")
println(v1.contains(8))
println(v1.contains(-2))
println("================================")
println(v2.get(0))
// println(v2.get(11))
println("================================")
println(v1.getFrom())
println(v4.getFrom())
println(v1.getTo())
println(v4.getTo())
println("================================")
println(v1.isReverse())
println(v4.isReverse())
println("================================")
println(v1.size())
println(v2.size())
println("================================")
println(v1.subList(1, 4))
}
|
运行结果:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
| [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
[1, 2, 3, 4, 5, 6, 7, 8, 9]
[a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x]
[10, 9, 8, 7, 6, 5, 4, 3, 2, 1]
[x, w, v, u, t, s, r, q, p, o, n, m, l, k, j, i, h, g, f, e, d, c, b, a]
================================
true
false
================================
1
================================
1
1
10
10
================================
false
true
================================
10
9
================================
[2, 3, 4]
|
Groovy 列表 (Lists)
列表是用于存储数据项集合的结构。在 Groovy 中,List 保存了一系列对象引用。List 中的对象引用占据序列中的位置,并通过整数索引来区分。列表文字表示为一系列用逗号分隔并用方括号括起来的对象。
要处理列表中的数据,我们必须能够访问各个元素。 Groovy 列表使用索引操作符 [] 索引。列表索引从零开始,这指的是第一个元素。以下是一些列表的示例:
[11,12,13,14]
- 整数值列表['Angular','Groovy','Java']
- 字符串列表[1,2,[3,4],5]
- 嵌套列表['Groovy',21,2.11]
- 异构的对象引用列表[]
- 一个空列表
List 中的方法
- boolean add(Object value) 将新值附加到此列表的末尾
- void add(int index,Object value) 将新值附加到列表中的特定位置 index
- boolean contains(Object value) 如果此列表包含指定的值 value,则返回 true
- Object get(int index) 返回此列表中指定位置 index 处的元素
除了通过 get(index) 方法,也可以通过 [index]
来访问 - boolean isEmpty() 如果此列表不包含元素,则返回 true。
- List minus(Collection collection) 创建一个由原始元素组成的新列表,而不是集合中指定的元素。(原 List 元素不变)
- List plus(Collection collection) 创建由原始元素和集合中指定的元素组成的新列表。(原 List 元素不变)
- Object pop() 从此列表中删除最后一个项目,如果没有值了会抛异常
- Object remove(int index) 删除此列表中指定位置 index 的元素。
- List reverse() 创建与原始列表的元素相反的新列表。
- int size() 获取此列表中的元素数。
- List sort() 返回原始列表的排序副本。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
| ////定义list
////def list=new ArrayList()
//def list=[1,2,3,4,5]
//println list.class
//println list.size()
////定义数组
//def array=[1,2,3,4,5] as int[]
//
////1.添加
//list.add(6)
//list<<2
//println list
//def plusList=list+5
////println plusList
//plusList.add(3,9)
////println plusList
//2.删除
//list.remove(2) //删除下标位置的对象
//list.remove((Object)2)
//list.removeElement(2)
//list.removeAll{
// return it%2!=0
//}
//println list-[2,4]
////3.查找
//def findList=[5,-2,1,4,3]
////查找满足条件的第一个数据
//int result=findList.find{
// return it%2 == 0
//}
//println result
////查找所有满足条件的数据
//def result2=findList.findAll({
// return it%2 !=0
//})
//println result2
//
////查找是否有满足条件的数据
//def result3=findList.any{
// return it%2 ==0
//}
//println result3
//
////查找是否全部满足条件
//def result4=findList.every{
// return it%2 ==0
//}
//println result4
//
////查找最大值与最小值
//def result5=findList.min{
// return Math.abs(it)
//}
//println result5
//def result6=findList.max{
// return Math.abs(it)
//}
//println result6
////统计
//int result7=findList.count{
// return it>0
//}
//println result7
//4.排序
def sortList=[5,-2,1,4,3]
sortList.sort({a,b ->
a == b ? 0 : Math.abs(a)>Math.abs(b) ? 1 :-1
})
println sortList
//对象排序
def sortStringList=['aaaaa','bbbb','c','ddd','ee']
sortStringList.sort({it ->
return it.size()
})
println sortStringList
|
Groovy 映射 (Maps)
映射 (Map,也称为关联数组,字典,表和散列),是对象引用的无序集合。Map 集合中的元素由键值访问。Map 中使用的键可以是任意类。当我们插入到 Map 集合中时,需要两个值:键和值。
以下是一些映射的例子:
- [‘TopicName’:’Lists’,’TopicName’:’Maps’] - 具有 TopicName 作为键的键值对的集合及其相应的值。
- [:] - 空映射。
Map 中的方法
- boolean containsKey(Object key) 此映射是否包含此 key?
- boolean containsKey(Object value) 此映射是否包含 value?
- Object get(Object key) 查找此 Map 中的键并返回相应的值。如果此映射中没有键的条目,则返回 null。
- Set keySet() 获取此映射中的一组 key。
- Object put(Object key, Object value) 将指定的值与此映射中的指定键相关联。 如果此映射先前包含此键的映射,则旧值将替换为指定的值。返回值,返回之前的 key 位置的 value,如果第一次插入那么 value 为 null
- int size() 返回此映射中的键值映射的数量。
- Collection values() 返回此映射中包含的 value 的集合视图。(返回所有的 value 值)
案例 1:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
| private static void testMap() {
def map = ["topicName1": "list", 'topicName': 'map', null: "nullValue"]
println(map) // [topicName1:list, topicName:map, null:nullValue]
println(map.containsKey("topicName")) // true
println(map.containsKey("null")) // true
println(map.containsValue("map")) // true
println(map.put("key1", "value2")) // null
println(map.put("key1", "value1")) // value2
println(map) // [topicName1:list, topicName:map, null:nullValue, key1:value1]
println(map.size()) // 4
println(map.values()) // [list, map, nullValue, value1]
}
private static void testMap() {
def map = ["topicName1": "list", 'topicName': 'map', null: "nullValue"]
println(map) // [topicName1:list, topicName:map, null:nullValue]
println(map.containsKey("topicName")) // true
println(map.containsKey("null")) // true
println(map.containsValue("map")) // true
println(map.put("key1", "value2")) // null
println(map.put("key1", "value1")) // value2
println(map) // [topicName1:list, topicName:map, null:nullValue, key1:value1]
println(map.size()) // 4
println(map.values()) // [list, map, nullValue, value1]
}
|
案例 2:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
| ////定义与读取
//def colors=[red:'ff0000',green:'00ff00',blue:'0000ff']
//println colors['red']
//println colors.red
////如果使用colors.class 会把class当成一个键
////class java.util.LinkedHashMap
//println colors.getClass()
//
////添加普通对象
//colors.yellow='ffff00'
//println colors
////添加集合对象
//colors.map = [key1:1,key2:2]
//println colors.toMapString()
//遍历map
def teachers = [
1: [number: '001', name: 'jett'],
4: [number: '004', name: 'alven'],
3: [number: '003', name: 'lance'],
2: [number: '002', name: 'leo'],
6: [number: '006', name: 'allen'],
5: [number: '005', name: 'zero'],
7: [number: '007', name: 'derry'],
8: [number: '008', name: 'jett']
]
//用键值对的方式
//teachers.each {def key,def value ->
// println "key=${key}---value=${value}"
//}
//用entry对象的方式
//teachers.each {def teacher ->
// println "key=${teacher.key}---value=${teacher.value}"
//}
//带索引的方式
//teachers.eachWithIndex{ def teacher,int index->
// println "index=${index}---key=${teacher.key}---value=${teacher.value}"
//}
//teachers.eachWithIndex{ def key,def value,int index->
// println "index=${index}---key=${key}---value=${value}"
//}
//map的查找
//def entry=teachers.find{def teacher ->
// return teacher.value.name=='jett'
//}
//println entry
//def entry=teachers.findAll{def teacher ->
// return teacher.value.name=='jett'
//}
//println entry
//def count=teachers.count{def teacher ->
// return teacher.value.name=='jett'
//}
//println count
//实现嵌套查询
def number=teachers.findAll{def teacher->
return teacher.value.name=='jett'
}.collect(){
return it.value.number
}
println number.toListString()
//实现分组查询
def group=teachers.groupBy {def teacher ->
return teacher.value.name=='jett' ? 'group1' : 'group2'
}
println group.toMapString()
//排序 注意:map会返回一个新的map list是在原来的list中进行排序
def sort=teachers.sort{def t1,def t2 ->
return t1.key > t2.key ? 1 : -1
}
println sort.toMapString()
|
Groovy 日期和时间
类 Date 表示特定的时刻,具有毫秒精度。 Date 类有两个构造函数
- Date() 当前日期和时间
- Date (长毫秒) 分配一个 Date 对象并将其初始化以表示自标准基准时间(称为 “ 该历元 “,即 1970 年 1 月 1 日,00:00:00 GMT)起指定的毫秒数。
Date 中方法
- after() 测试此日期是否在指定日期之后。
- before() 测试此日期是否在指定日期之前。
- equals() 比较两个日期的相等性。当且仅当参数不为 null 时,结果为 true,并且是表示与该对象时间相同的时间点(毫秒)的 Date 对象。
- compareTo() 比较两个日期的顺序。
- getTime() 返回自此 Date 对象表示的 1970 年 1 月 1 日,00:00:00 GMT 以来的毫秒数。
- setTime() 设置此 Date 对象以表示一个时间点,即 1970 年 1 月 1 日 00:00:00 GMT 之后的时间毫秒。
- toString() 将此 Date 对象转换为字符串
Groovy 正则表达式
正则表达式用于在文本中查找子字符串的模式。Groovy 使用 ~regex
表达式本地支持正则表达式。regex 包含的文本表示用于比较的表达式。
当 Groovy 运算符 =~ 在 if 或 while 语句中作为判断表达式时,左侧的 String 操作数与右侧的正则表达式操作数匹配,判断条件为 true
1
2
3
| if ("hacketfafdf" =~ "hacket") {
println("true") // true
}
|
正则表达式规则,可以使用的特殊字符
- 有两个特殊的位置字符用于表示一行的开始和结束:
^
和 $
- 正则表达式也可以包括量词:
+
表示一次或多次,应用于表达式的前一个元素;*
表示零个或多个出现;?
表示零或一次 - 元字符
{
和 }
用于匹配前一个字符的特定数量的实例 - 在正则表达式中,
.
可以表示任何字符。(通配符) - 正则表达式可以包括字符类。一组字符可以作为简单的字符序列,包含在元字符
[
和 ]
中,如:[aeiou]
。对于字母或数字范围,可以使用 [a-z]
或 [a-mA-M]
中的 -
分隔符。字符类的补码由方括号内的前导插入符号表示,如 [^a-z]
,并表示除指定的字符以外的所有字符。
Groovy 异常处理
同 Java 异常处理机制
Groovy 面向对象
同 Java 面向对象
1
2
3
4
5
6
| class/接口
抽象
封装
继承
多态
trait
|
接口
- 接口中不能定义非 public 方法的
1
2
3
4
5
6
7
8
| /**
* 接口中不能定义非public方法的
*/
interface Action {
void eat()
void drink()
void play()
}
|
类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| package objectorention
// 1.在groovy中所有的类型 默认都是public
// 2.所有的类都是继承自GroovyObject
class Person implements DefaultAction{
String name
Integer age
def increateseAge(Integer year){
this.age+=year
}
@Override
void eat() {
}
}
|
创建对象和使用对象:
1
2
3
4
5
6
7
8
9
10
| package objectorention
def person=new Person(name:'jett',age:18)
println "name="+person.name+" age="+person.age
println "name="+person.getName()+" age="+person.getAge()
person.increateseAge(10)
println "name="+person.name+" age="+person.age
person.play()
|
trait
1
2
3
4
5
6
7
8
| package objectorention
trait DefaultAction {
abstract void eat()
void play(){
println 'I can play!'
}
}
|
Groovy 泛型
同 Java 泛型
Groovy 之 XML
XML Markup Builder
MarkupBuilder 用于构造整个 XML 文档。通过首先创建 XML 文档类的对象来创建 XML 文档。一旦创建了对象,可以调用伪方法来创建 XML 文档的各种元素。
1
2
3
4
5
6
7
8
9
10
| def markBuilder = new MarkupBuilder()
markBuilder.collection(shelf: 'New Arrivals') {
movie(title: 'Enemy Behind')
type('War, Thriller')
format('DVD')
year('2003')
rating('PG')
stars(10)
description('Talk about a US-Japan war')
}
|
结果:
1
2
3
4
5
6
7
8
9
| <collection shelf='New Arrivals'>
<movie title='Enemy Behind' />
<type>War, Thriller</type>
<format>DVD</format>
<year>2003</year>
<rating>PG</rating>
<stars>10</stars>
<description>Talk about a US-Japan war</description>
</collection>
|
- markBuilder.collection() 这是一个标记生成器,用于创建
<collection></collection>
的头 XML 标签 - movie(title : ‘Enemy Behind’) - 这些伪方法使用此方法创建带有值的标记的子标记。通过指定一个名为 title 的值,这实际上表示需要为该元素创建一个属性。
- 向伪方法提供闭包以创建 XML 文档的剩余元素。
- 初始化类 MarkupBuilder 的默认构造函数,以便将生成的 XML 发布到标准输出流
XmlParser
XmlParser 来解析 XML 文档
案例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
| import groovy.xml.MarkupBuilder
import groovy.xml.XmlSlurper
final String xml='''
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.jvm_demo_20200601">
<test>12345</test>
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".MainActivity2">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
'''
////解析XML数据
//def xmlSluper=new XmlSlurper()
//def result=xmlSluper.parseText(xml)
//println result.@package
//println result.test.text()
////读取有域名空间的节点
//result.declareNamespace('android':'http://schemas.android.com/apk/res/android')
//println result.application.@'android:allowBackup'
//println result.application.activity[0].@'android:name'
//println result.application.activity[1].@'android:name'
//
////遍历XML节点
//result.application.activity.each{activity ->
// println activity.@'android:name'
//}
/**
* 生成XML格式数据
* <html>
* <title id='123',name='android'>xml生成
* <person></person>
* </title>
* <body name='java'>
* <activity id='001' class='MainActivity'>abc</activity>
* <activity id='002' class='SecActivity'>abc</activity>
* </body>
* </html>
*/
def sw=new StringWriter()
def xmlBuilder=new MarkupBuilder(sw)
xmlBuilder.html(){
title(id:'123',name:'android','xml生成'){
person()
}
body(name:'java'){
activity(id:'001',class:'MainActivity','abc')
activity(id:'002',class:'SecActivity','abc')
}
}
println sw
|
Groovy 之 JSON
用 Groovy 来解析和生成 JSON 对象
API | 功能 |
---|
JsonSlurper | JsonSlurper 是一个将 JSON 文本或阅读器内容(reader content)解析为 Groovy 中的数据结构(如 Map、List、Integer、Double、Boolean 和 String) |
JsonOutput | 代表了将 Groovy 对象序列化成 JSON 字符串 |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
| import groovy.json.JsonOutput
import groovy.json.JsonSlurper
// 对象转成json字符串
def list=[new Person(name:'jett',age:18),
new Person(name:'lance',age:18)]
println JsonOutput.toJson(list)
// 格式化
def json=JsonOutput.toJson(list)
println JsonOutput.prettyPrint(json)
// json字符串转成对象
def jsonSluper=new JsonSlurper()
def object=jsonSluper.parse("[{\"age\":18,\"name\":\"jett\"},{\"age\":18,\"name\":\"lance\"}]".getBytes())
println object
def object2=jsonSluper.parse("[{\"abc\":\"jett\"}]".getBytes())
println object2.abc
class Person {
String name
Integer age
}
|
生成 json
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| static def json(List<String> atUsers) {
def json = new JsonBuilder()
json {
msgtype "markdown"
markdown {
title "title"
text "text"
}
at {
if (!atUsers.isEmpty()) {
atMobiles(atUsers)
}
isAtAll false
}
}
return json.toString()
}
|
输出:
1
| {"msgtype":"markdown","markdown":{"title":"title","text":"text"},"at":{"atMobiles":["134","135"],"isAtAll":false}}
|
Groovy 注解 (Annotations)
用@interface
表示,类似 Java。
1
2
3
4
5
6
7
8
| @interface Simple { // 不能嵌套在class内部
int status() // default 0
}
@Simple(status = 1)
static class User {
String username
int age
}
|
Groovy 闭包 (Closure)
见下面的 Groovy语法之闭包.md
Groovy 对 Java 的优化
- 表达式后面的分号是可选的
- 每个类、构造器和方法默认是 public 的
- groovy 方法体中的最后一个表达式的值会被作为返回值
- groovy 编译器自动加上 getter/setter 方法
- 类的属性可以通过点 (.) 来获取,底层是 groovy 自动调用 getter/setter 方法
- 用 == 比较两个类的实例,底层调用的是 equals() 方法,这个操作也避免了 npe 异常
可选的括号
Groovy 中如果方法签名需要至少一个参数的话,则方法调用可以省略括号。
1
2
| initProjectVersion(1,2)
initProjectVersion 1,2
|
字符串
1
2
3
4
5
6
7
8
9
| // 1、单引号
def myStr1 = `This is a single-quoted String`
// 2、双引号,也叫GString。可${str}来表示
def myStr2 = "This is a double-quoted String"
// 3、三引号
def myStr3 = """
This is
a multiline String
"""
|
命名参数
一个类没有默认构造器,Groovy 会调用类的默认构造器,然后为每个参数调用对应的 setter 方法
1
| ProjectVersion pv = new ProjectVersion(major:1,minor:10)
|
闭包
- 闭包参数
隐式闭包参数 it
显示闭包参数 - 闭包返回值
- 闭包作为方法参数 Closure
- 闭包委托
闭包代码在委托的闭包上执行。默认的,这个委托就是闭包的所有者(你在 Groovy 脚本中定义了一个闭包,那么这个闭包的所有者就是 groogy.lang.Script 实例)。闭包的隐式变量 delegate 允许你重新定义默认的所有者。
Groovy 新的特性
trait
trait 是语言的结构构造,它允许:
- 行为的组成
- 接口的运行时实现
- 和静态类型检查/编译相兼容
它们可以被看作是承载默认实现和状态的接口,用 trait
关键字定义。
1
2
3
4
5
| trait Marks {
void displayMarks() {
println("display Marks")
}
}
|
然后可以用 implement 关键字类似接口的方式实现 trait
1
2
3
4
5
6
7
8
9
| trait Marks {
void displayMarks() {
println("display Marks")
}
}
static class StuMarks implements Marks {
int studentID
int marks1;
}
|
trait 可以实现接口
trait 可以实现接口,在这种情况下,使用 implements 关键字来实现声明的接口。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| interface Total {
void displayTotal()
}
trait Marks implements Total {
void displayMarks() {
println("display Marks")
}
@Override
void displayTotal() {
println("display total")
}
}
class StuMarks implements Marks {
int studentID
int marks1
}
|
trait 可以定义属性
trait 可以定义属性:
1
2
3
4
5
6
7
| trait Marks {
int Marks1
void displayMarks() {
this.Marks1 = 10
println("display Marks:" + Marks1)
}
}
|
trait 行为组合(Composition of Behaviors)
类似接口一样,可以被同一个 class 实现多个 trait,然后该 class 就具备了这两个 trait 的行为。
1
2
3
4
5
6
7
8
9
10
11
12
13
| trait Marks {
void DisplayMarks() {
println("Marks1");
}
}
trait Total {
void DisplayTotal() {
println("Total");
}
}
class Student implements Marks,Total {
int StudentID
}
|
继承 trait(Extending Traits)
trait 可以继承另外一个 trait,使用 extends
关键字。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
| class Example {
static void main(String[] args) {
Student st = new Student();
st.StudentID = 1;
println(st.DisplayMarks());
}
}
trait Marks {
void DisplayMarks() {
println("Marks1");
}
}
trait Total extends Marks {
void DisplayMarks() {
println("Total");
}
}
class Student implements Total {
int StudentID
}
|
Groovy 闭包
闭包的定义
闭包是 Groovy 中非常重要的一个数据类型或者说一种概念。闭包是一种数据类型,它代表了一段可执行的代码。闭包的语法定义:
1
| { [closureParameters -> ] statements }
|
- 其中
[closureParameters->]
部分是一个可选的以逗号分隔的参数列表 - statements 可以为空,一行或者多行代码
当参数列表确定时,->
是必须的,他负责把参数和闭包的代码块分开,代码块可以由一行或者多行语句组成
一些闭包常用的定义形式:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| { item++ }
{ -> item++ }
{ println 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()
}
|
如果闭包没有定义参数的话,则隐含有一个参数,这个参数的名字交 it,和 this 作用类似,it 代表闭包的参数。
闭包作为一个对象
闭包实质上是一个 groovy.lang.Closure
类的实例,虽然他是一个代码块,但是他可以为任何一个变量或者字段赋值,闭包中可以包含代码的逻辑,闭包中的最后一行语句,表示该闭包的返回值,不论该语句是否有 return 关键字
1
2
3
4
5
6
7
8
9
10
11
| // 可以将闭包分配给一个变量,这个变量就是groovy.lang.Closure的一个实例
def listener = { e -> println "Clicked on $e.source" }
assert listener instanceof Closure
// 如果不使用def,可以将一个闭包分配给一个类型为groovy.lang.Closure的变量
Closure callback = { println 'Done!' }
// 可以通过使用groovy.lang.Closure的泛型来指定闭包的返回类型
Closure<Boolean> isTextFile = {
File it -> it.name.endsWith('.txt')
}
|
调用闭包
下面是一段简短的 closure:
1
2
3
4
5
| void test1() {
def clos = { println "i am a closure!" }
clos.call()
// clos() // 这个也可以调用
}
|
上面 { println "i am a closure!"}
称之为闭包。这段代码块(闭包)可以通过 闭包.call()
或 闭包()
或来执行。
- 闭包作为一个匿名的代码块,可以像方法那样被调用
- 闭包内的代码只有在闭包被调用时才会执行,调用可以像常规方法那样来完成
- 可以显式的调用 call 方法调用
- 和方法不一样的是,闭包始终有一个返回值
闭包的参数遵循与常规方法的参数相同的原则 (参数用 ,
分隔):
- 可选的类型
- 名字
- 可选的默认值
闭包的显式参数
闭包也可以包含形式参数,以使它们更有用,就像 Groovy 中的方法一样:
1
2
3
4
| void test1() {
def clos = { param -> println "i am a closure!${param}" }
clos.call("hacket")
}
|
上面的代码用 $param
或 ${param}
是 closure 接收一个参数,当通过 call 调用闭包时,可以传递一个参数给闭包。
上面的代码也等价于:
1
2
3
4
| void test1() {
def clos = { println "i am a closure!$it" }
clos.call("hacket")
}
|
示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
| // 只有1个参数,只有参数名,没有类型
def closureWithOneArg = { str -> str.toUpperCase() }
assert closureWithOneArg('groovy') == 'GROOVY'
// 只有1个参数,有参数类型和参数名字
def closureWithOneArgAndExplicitType = { String str -> str.toUpperCase() }
assert closureWithOneArgAndExplicitType('groovy') == 'GROOVY'
// 有2个参数,只有参数名,没有类型
def closureWithTwoArgs = { a,b -> a+b }
assert closureWithTwoArgs(1,2) == 3
// 有2个参数,有参数名和参数类型
def closureWithTwoArgsAndExplicitTypes = { int a, int b -> a+b }
assert closureWithTwoArgsAndExplicitTypes(1,2) == 3
// 有2个参数,第1个参数只有名字,第2个参数有参数类型和名字
def closureWithTwoArgsAndOptionalTypes = { a, int b -> a+b }
assert closureWithTwoArgsAndOptionalTypes(1,2) == 3
// 有2个参数,第1个参数有参数类型和参数名字,第2个参数有参数类型、参数名字和参数默认值
def closureWithTwoArgAndDefaultValue = { int a, int b=2 -> a+b }
assert closureWithTwoArgAndDefaultValue(1) == 3
|
闭包的隐式参数
当一个闭包没有明确定义一个参数列表(使用 ->
)时,闭包总是定义一个隐式参数,并将其命名为 it。
1
2
3
4
5
| def greeting = { "Hello, $it!" }
assert greeting('Patrick') == 'Hello, Patrick!'
// 等同于
def greeting = { it -> "Hello, $it!" }
assert greeting('Patrick') == 'Hello, Patrick!'
|
如果想声明一个不接受参数的闭包,并且必须限制为没有参数的调用,那么必须声明一个明确的空参数列表:
1
2
3
4
| def magicNumber = { -> 42 }
// this call will fail because the closure doesn't accept any argument
magicNumber(11)
|
- 当闭包作为闭包或方法的最后一个参数,可以将闭包从参数圆括号中提取出来接在最后,如果闭包是唯一的一个参数,则闭包或方法参数所在的圆括号也可以省略
- 对于有多个闭包参数的,只要是在参数声明最后的,均可以按上述方式省略
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| def runTwice = { a, c -> c(c(a)) }
assert runTwice( 5, {it * 3} ) == 45 //usual syntax
assert runTwice( 5 ){it * 3} == 45
//when closure is last param, can put it after the param list
def runTwiceAndConcat = { c -> c() + c() }
assert runTwiceAndConcat( { 'plate' } ) == 'plateplate' //usual syntax
assert runTwiceAndConcat(){ 'bowl' } == 'bowlbowl' //shortcut form
assert runTwiceAndConcat{ 'mug' } == 'mugmug'
//can skip parens altogether if closure is only param
def runTwoClosures = { a, c1, c2 -> c1(c2(a)) }
//when more than one closure as last params
assert runTwoClosures( 5, {it*3}, {it*4} ) == 60 //usual syntax
assert runTwoClosures( 5 ){it*3}{it*4} == 60 //shortcut form
|
默认参数
1
2
3
4
5
6
7
| // 闭包支持默认参数
cClosure("hello","clat") // hello, clat!
def dClosure = {
name,address = "shenzhen" ->
println "${name},${address}!"
}
dClosure("hacket"); // hacket,shenzhen!
|
闭包和变量 (Closures and Variables)
闭包代码块中可以引用外部定义的变量:
1
2
3
4
5
6
| def str1 = "Hello"
def clos = { param -> println "${str1} $param" }
clos.call(" World!") // Hello World!
str1 = "Welcome"
clos(" World!") // Welcome World!
|
带返回值的闭包
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
| //定义与使用
//无参数的闭包
//def closure={
// println "hello groovy!"
//}
//closure()
//closure.call()
//带参数的闭包
//def closure={String name,int age->
// println "hello ${name}:age ${age}"
//}
//closure.call("jett",18)
//带默认参数
//def closure={
// println "hello ${it}"
//}
//closure.call("jett")
//闭包的返回值
def closure={
println "hello ${it}"
return "123"
}
def result=closure.call("jett")
println "result="+result
|
可变参数
闭包可以象任何其他方法一样声明可变参数
1
2
3
4
5
6
7
8
9
| 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'
|
应用闭包
在方法中使用闭包
- 闭包也可以作为方法的参数。
在 Groovy 中,很多用于数据类型(如 List 和 Map)的内置方法都有闭包作为参数类型。
1
2
3
4
5
6
7
8
9
| static void test3() {
def str1 = "Hello"
def clos = { param -> println "$str1 $param" }
display(clos)
}
def static display(clo) {
clo.call("Inner")
}
|
- 匿名内联函数,也称为一个闭包
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
| /**
* 匿名内联函数,也称为一个闭包。
* 基本类型相关的API
*/
int x=fab(5)
int fab(int number){
int result=1;
1.upto(number,{num -> result *= num})
return result
}
println x;
int fab2(int number){
int result=1
number.downto(1){
num-> result*=num
}
return result
}
println fab2(5)
int sum(int number){
int result=0;
number.times {
num-> result+=num;
}
return result
}
println sum(5)
|
和 String 相关的 API
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
| /**
* 和String相关的API
*/
String str="2 and 3 is 5"
//each遍历
//str.each {
// String s->print s.multiply(2)
//}
//find查找符合条件的第一个字符
//println str.find{
// String s->s.isNumber()
//}
//findAll 查找符合条件的所有字符
//def list=str.findAll{
// String s-> s.isNumber()
//}
//println list.toListString()
//any 查找是否存在符合条件的字符
//def result=str.any{
// String s->s.isNumber()
//}
//println result
//every查找是否所有字符都符合条件
//def result=str.every{
// String s->s.isNumber()
//}
//println result
//对str的每一位单独操作后的结果保存到一个集合中
def list=str.collect{
it.toUpperCase()
}
println list.toListString()
|
List/Map 中的闭包
1
2
3
4
5
6
7
8
9
10
11
12
13
| static void test4() {
def list = [11, 23, 12, 14]
list.each { println it }
list.each { num ->
if (num % 2 == 0) {
println("偶数:" + num)
}
}
def map = ["TopicName": "Maps", "TopicDescription": "Methods in Maps"]
map.each { println it }
map.each { println "${it.key} maps to $it.value" }
}
|
- find()
find 方法查找集合中与某个条件匹配的第一个值 - findAll()
它找到接收对象中与闭合条件匹配的所有值 - any()
集合中至少有一个元素满足条件 - every()
集合中所有的元素满足条件 - collection()
该方法通过集合收集迭代,使用闭包作为变换器将每个元素转换为新值。(通过 closure 表达式变换为一个新的集合)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| def list2 = [1, 2, 0, 43, 34]
def value = list2.find({ num -> num >= 2 })
println(value) // 2
def val2 = list2.findAll { num -> num >= 2 }
val2.each { println it } // 2 43 34
def any = list2.any { it > 2 }
println(any) // true
def every = list2.every { it > 2 }
println(every) // false
def list3 = list2.collect { it * it }
list3.each { println(it) } // 1 4 0 1849 1156
|
Groovy Bean 和闭包
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
| // groovy bean
class GroovyBeanExample {
private String name
}
def bean = new GroovyBeanExample();
bean.name = "my name is hacket!"
println bean.name
// 闭包
// 闭包是一个可执行的代码块。
// 1、闭包
def aClosuer = {
println "Hello Closure!";
}
println aClosuer; // my$_run_closure1@962257c
// 闭包的调用
aClosuer.call(); // 使用call()调用闭包 Hello Closure!
aClosuer(); // 调用闭包的简写方式,类似于方法调用。 Hello Closure!
// 闭包在调用的时候才会执行
// 2、隐式参数化闭包
def bClosure = {
println "Hello ${it}" //it 是闭包的单个隐含参数。
}
bClosure.call("clat") // Hello clat
bClosure("clat") // Hello clat
bClosure "clat" // Hello clat
// 3、显示参数
def cClosure = {
name,address ->
println "${name},${address}!"
}
// 闭包支持默认参数
cClosure("hello","clat") // hello, clat!
def dClosure = {
name,address = "shenzhen" ->
println "${name},${address}!"
}
dClosure("hacket"); // hacket,shenzhen!
println('=============================');
// 4、闭包作用域
def name = "hacket..."
def eClosure = {
println name;
}
eClosure(); // hacket...
def aMethod(Closure closure){
name = "a hacket"; // 这里的name只是方法的局部变量,闭包不能访问
closure();
}
aMethod(eClosure); // hacket...
def fClosure = {
name = "f hacket"; // 这里的name是闭包里面的,实就是可执行片段修改了外围的一个变量
eClosure();
}
fClosure(); // f hacket
// 5、闭包返回值
def gClosure = {
number ->
return number * 2;
}
println gClosure(3); // 6
// 6、闭包与集合、字符串
// 遍历集合
def nameList = ["clat","escaflone","Aldern"]
nameList.each{
myname ->
print myname+"_"
}
println ""
// clat_escaflone_Aldern_
// 遍历map
def nameMap = [1:'clat',2:'escaflone',3:'Aldern']
nameMap.each{
map ->
println map.key +":"+ map.value
}
// 1:clat
// 2:escaflone
// 3:Aldern
// 遍历Range
(1..<10).each{
print it+"-"
}
// 1-2-3-4-5-6-7-8-9-
println ""
// 遍历String
"hacket".each{
print it+"~"
}
// h~a~c~k~e~t~
println ""
// 7、闭包嵌套
def outClosure = {
country ->
println "country-->"+country;
def innerClosure = {
city ->
println "city-->"+city
}
innerClosure("shenzhen");
}
outClosure("China");
// country-->China
// city-->shenzhen
|
闭包委托策略(this,owner,deleate)
Groovy 的闭包比 Java 的 Lambda 表达式功能更强大。原因就是 Groovy 闭包可以修改委托对象和委托策略。这样 Groovy 就可以实现非常优美的领域描述语言(DSL)了。Gradle 就是一个鲜明的例子。
Groovy 闭包有三个相关的对象:
- this 即闭包定义所在的类
- owner 即闭包定义所在的对象或闭包
- delegate 即闭包中引用的第三方对象
this
this 代表闭包定义所在的类;在闭包中,调用 getThisObject()
将返回闭包定义的闭包类,和显式的调用 this
一样
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
| class Enclosing {
def run() {
def whatIsThisObject = {
println("Closure.this=${this}") // this=me.hacket.groovy.Enclosing@1603cd68
getThisObject()
}
assert whatIsThisObject() == this // true
println("Enclosing.this=${this}") // this=me.hacket.groovy.Enclosing@1603cd68
def whatIsThis = { this }
assert whatIsThis() == this // true
}
}
class EnclosedInInnerClass {
class Inner {
Closure cl = { this } // 这个this为Inner对象,返回的就是inner对象
}
void run() {
def inner = new Inner()
assert inner.cl.call() == inner // true
}
}
|
owner
owner 代表闭包定义所在的对象或闭包
delegate
可以使用 delegate 属性或者调用 getDelegate 方法来访问闭包中的 delegate,delegate 在 Groovy 中是一个很重要的概念,delegate 需要我们手动指定;默认 delegate 和 owner 一样
1
2
3
4
5
6
7
8
9
10
11
12
| class Enclosing {
void run() {
def cl = { getDelegate() }
def cl2 = { delegate }
assert cl() == cl2()
assert cl() == this
def enclosed = {
{ -> delegate }.call()
}
assert enclosed() == enclosed
}
}
|
闭包的 delegate 可以被改变成任何对象。 我们通过创建两个不是彼此的子类的类来说明这一点,但都定义了一个名为 name 的属性:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
| class Person {
String name
}
class Thing {
String name
}
def p = new Person(name: 'Norman')
def t = new Thing(name: 'Teapot')
// 然后定义一个闭包通过delegate来调用name属性
def upperCasedName = { delegate.name.toUpperCase() }
// 然后通过更改闭包的委托,可以看到目标对象将会改变
upperCasedName.delegate = p
assert upperCasedName() == 'NORMAN'
upperCasedName.delegate = t
assert upperCasedName() == 'TEAPOT'
|
Delegation strategy(委托策略)
无论什么时候,在闭包中,访问某个属性时都没有明确地设置接收者对象,那么就会调用一个委托策略
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| class Person2 {
String name
}
static void main(String[] args) {
def p = new Person2(name: 'Igor')
def cl = { name.toUpperCase() }
cl.delegate = p
// println("cl.owner=${cl.owner}, name=${cl.owner.name}") // MissingPropertyException
println("cl.delegate=${cl.delegate}, name=${cl.delegate.name}") // cl.delegate=me.hacket.groovy.Person2@6ce86ce1, name=Igor
// name property be resolved transparently on the delegate object!
assert cl() == 'IGOR' // true
}
|
调用 name 属性并没有执行是谁的 name 属性,然后把闭包 cl 的 delegate 设置为 p,这里没有显式的给 delegate 设置一个接收者,但是能成功访问到 name 属性,因为相应的属性解析策略:
- Closure.OWNER_FIRST,默认策略,首先从 owner 上寻找属性或方法,找不到则在 delegate 上寻找。
- Closure.DELEGATE_FIRST,和上面相反。首先从 delegate 上寻找属性或者方法
- Closure.OWNER_ONLY,只在 owner 上寻找,delegate 被忽略。
- Closure.DELEGATE_ONLY,和上面相反。只在 delegate 上寻找,owner 被忽略。
- Closure.TO_SELF,高级选项,让开发者自定义策略。
示例 1(Closure.OWNER_FIRST):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
| class Person3 {
String name
def pretty = { "My name is $name" }
String toString() {
pretty.call()
}
}
class Thing {
String name
}
static void main(String[] args) {
def p = new Person3(name: 'Sarah')
def t = new Thing(name: 'Teapot')
assert p.toString() == 'My name is Sarah'
p.pretty.delegate = t // 默认Closure.OWNER_FIRST,从owner寻找
assert p.toString() == 'My name is Sarah' // true
}
|
Person 和 Thing 都定义了 name 属性,使用默认策略,首先会在 Owner 上寻找,所以即使我们把 delegate 换成 t,输出结果还是一样的
示例 2(Closure.DELEGATE_FIRST):
1
2
| p.pretty.resolveStrategy = Closure.DELEGATE_FIRST
assert p.toString() == 'My name is Teapot'
|
把委托策略改成 Closure.DELEGATE_FIRST,则会首先去 delegate 寻找 name 属性,如果没有找到,再去 owner 上寻找,但是 delegate 有 name 的定义,所以输出结果为 “My name is Teapot”
示例 3(delegate first vs delegate only):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
| class Person {
String name
int age
def fetchAge = { age }
}
class Thing {
String name
}
def p = new Person(name:'Jessica', age:42)
def t = new Thing(name:'Printer')
def cl = p.fetchAge
cl.delegate = p
assert cl() == 42
cl.delegate = t
assert cl() == 42
cl.resolveStrategy = Closure.DELEGATE_ONLY
cl.delegate = p
assert cl() == 42
cl.delegate = t
try {
cl()
assert false
} catch (MissingPropertyException ex) {
// "age" is not defined on the delegate
}
|
only 时,在对应的对象上找不到时,不会接着去别处找,而是抛出一个异常
测试
在普通类或方法中定义闭包,三者是相同的
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
| class Person {
def static classClouser = {
println "classClouser:" + this.hashCode()
println "classClouser:" + owner.hashCode()
println "classClouser:" + delegate.hashCode()
}
def static method() {
def classClouser = {
println "methodclassClouser:" + this.hashCode()
println "methodclassClouser:" + owner.hashCode()
println "methodclassClouser:" + delegate.hashCode()
}
classClouser.call()
}
}
public static void main(String[] args) {
Person.classClouser.call()
Person.method()
}
|
输出:
1
2
3
4
5
6
| classClouser:1321203216
classClouser:1321203216
classClouser:1321203216
methodclassClouser:1321203216
methodclassClouser:1321203216
methodclassClouser:1321203216
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
| /**
* 闭包的三个重要变量:this,owner,deleate
*/
//在同一个闭包中,都是相同的对象
//def scriptClouser = {
// println this//代表闭包定义处的类
// println owner//代表闭包定义处的类或者对象
// println delegate//代表任意对象,delegate默认为owner指向的对象
//}
//scriptClouser.call()
//在普通类或方法中定义闭包,三者是相同的
class Person {
// def static classClouser = {
// println "classClouser:" + this
// println "classClouser:" + owner
// println "classClouser:" + delegate
// }
//
// def static method() {
// def classClouser = {
// println "methodclassClouser:" + this
// println "methodclassClouser:" + owner
// println "methodclassClouser:" + delegate
// }
// classClouser.call()
// }
}
//Person.classClouser.call()
//Person.method()
//闭包内定义闭包 this内部对象 owner和delegate是外部对象
//def nestClouser = {
// def innerClouser = {
// println "innerClouser:" + this
// println "innerClouser:" + owner
// println "innerClouser:" + delegate
// }
// innerClouser.call()
//}
//nestClouser.call()
//修改默认的delegate对象
//Person p=new Person();
//def nestClouser = {
// def innerClouser = {
// println "innerClouser:" + this
// println "innerClouser:" + owner
// println "innerClouser:" + delegate
// }
// innerClouser.delegate=p;
// innerClouser.call()
//}
//nestClouser.call()
/**
* 闭包的委托策略
*/
class Student{
String name
def pretty={"My name is ${name}"}
String toString(){
pretty.call()
}
}
def student=new Student(name:'jett')
class Teacher{
String name
}
def teacher=new Teacher(name:'andy')
student.pretty.delegate=teacher
//闭包的委托策略
student.pretty.resolveStrategy=Closure.DELEGATE_FIRST
println student.toString()
|
Ref
Groovy 之 MOP(invokeMethod 和 methodMissing 方法)
invokeMethod 和 methodMissing
invokeMethod
方法可以分派所有的方法,包括一个类已经实现了的和未实现的方法;而它实现上面的功能是通过这个类实现 GroovyInterceptable
接口达到的,未实现 GroovyInterceptable
接口时,invokeMethod 和 methodMission 功能一致methodMissing
方法则只能分派一个类未实现的方法,无论它是否实现了 GroovyInterceptable
接口。
如果我们想让一个方法来管理一个类所有方法的调用,那么我们必须使用 “invokeMethod” 方法;如果我们只想通过一个方法来管理一个类的所有 “missing method”,即不能被分派出去的方法,那么使用 “methodMissing” 方法是比较有效的;当然,”invokeMethod” 方法也能实现 “methodMissing” 方法的功能。
测试
- invokeMethod 未实现 GroovyInterceptable
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| class InvokeTestor1 {
def hello() {
'invoke hello directly'
}
String invokeMethod(String name,Object args) {
return "invokeMethod unknown method $name(${args.join(',')})"
}
static main(args) {
def it = new InvokeTestor1()
println it.hello()
println it.foo("mark", 19)
}
}
|
输出:
1
2
| invoke hello directly
invokeMethod unknown method foo(mark,19)
|
- invokeMethod 实现 GroovyInterceptable
1
2
3
4
5
6
7
8
9
10
11
12
13
| class InvokeTestor1 implements GroovyInterceptable {
def hello() {
'invoke hello directly'
}
String invokeMethod(String name, Object args) {
return "invokeMethod unknown method $name(${args.join(',')})"
}
static main(args) {
def it = new InvokeTestor1()
println it.hello()
println it.foo("mark", 19)
}
}
|
输出:
1
2
| invokeMethod unknown method hello()
invokeMethod unknown method foo(mark,19)
|
- invokeMethod 实现 GroovyInterceptable 和为实现 GroovyInterceptable
1
2
3
4
5
6
7
8
9
10
11
12
13
| class InvokeTestor1 implements GroovyInterceptable {
def hello() {
'invoke hello directly'
}
def methodMissing(String name, args) {
return "methodMissing unknown method $name(${args.join(',')})"
}
static main(args) {
def it = new InvokeTestor1()
println it.hello()
println it.foo("mark", 19, 20, 30)
}
}
|
输出:
1
2
| invoke hello directly
methodMissing unknown method foo(mark,19,20,30)
|