文章

00.Groovy

00.Groovy

Groovy 语法

Groovy 基本语法

  1. 在 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
  1. Groovy 令牌 (Token)
    令牌可以是一个关键字、一个标识符、常量、字符串文字或符号
1
println("Hello World")

上面的代码中有,有两个令牌:关键字 println 和字符串 Hello World。

  1. Groovy 注释
    单行注释 //
    多行注释 /* */
    文档注释 /** */
  2. 分号
    像 Java 语言一样,需要分号在 Groovy 定义的多个语句之间进行区分。
  3. 标识符 (Identifiers)
    标识符被用来定义变量、函数或其他用户定义的变量。
1
2
3
def employeeName
def student
def stu_name
  1. 关键字 (Keywords)
    关键字作为名称建议是在 Groovy 编程语言中保留的特殊字。 下表列出了在 Groovy 中定义的关键字。
    300n8
  2. 空白 (Whitespaces)
    空白是在编程语言如 Java 和 Groovy 用来形容空格,制表符,换行符和注释术语。空格分隔从另一个声明的一部分,使编译器,其中一个元素标识的声明。
    例如,在下面的代码示例,存在关键字 def 和变量 x 之间的空白。这是为了让编译器知道 DEF 是需要被使用,并且是 x 应该是需要被定义的变量名的关键字。
1
def x = 5;
  1. 文字 (Literals)
    文字是在 groovy 中表示固定值的符号。Groovy 语言有符号整数,浮点数,字符和字符串。下面是一些在 Groovy 编程语言文字的例子:
1
2
3
4
12
1.45
a
aa
  1. 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
++自增运算,在自身值的基础上加 1INT X = 5;X ++;X 将得到 6
自减运算,在自身值的基础上减 1INT X = 5;X - -;X 将得到 4

关系运算符

关系运算符允许对象的比较。以下是在 Groovy 中可用的关系运算符:

  • ==
    测试两个对象之间是否相等
  • !=
    测试两个对象之间是否不等
  • «br >检查是否左边的对象是小于右边的对象
  • <=
    检查是否向左对象是小于或等于右边的对象

检查是否左边的对象比右边的对象大

  • =
    检查是否向左对象大于或等于右边的对象

逻辑运算符

逻辑运算符用于计算布尔表达式:

  • && 逻辑 “ 与 “ 运算
  •  逻辑 “ 或 “ 运算
  • !  逻辑 “ 非 “ 运算

位运算符

Groovy 中提供了四个位运算符。以下是在 Groovy 中可用的位运算符:

  • &
    按位 “ 与 “ 运算

  • 按位 “ 或 “ 运算
  • ^
    按位 “ 异或 “ 运算
  • ~
    按位 “ 反 “ 运算

赋值运算符

Groovy 语言也提供了赋值操作符。以下是在 Groovy 提供的赋值运算符:

  • +=
  • -=
  • *=
  • /=
  • (%)=

范围运算符

Groovy 支持范围的概念,并在..符号的帮助下提供范围运算符的符号:

1
def range = 0..5

这只是定义了一个简单的整数范围,存储到一个局部变量称为范围内的下限为 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++
}
  • for
    for 语句用于遍历一组值。
1
2
3
for (int i = 0; i < 5; i++) {
    print(" " + i)
}
  • for in
    for-in 语句用于遍历一组值。
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 提供了更简单的类来为文件提供以下功能:

  1. 读取文件
  2. 写入文件
  3. 遍历文件树
  4. 读取和写入数据对象到文件

始终可以使用下面列出的用于文件 I / O 操作的标准 Java 类:

  1. java.io.File
  2. java.io.InputStream
  3. java.io.OutputStream
  4. java.io.Reader
  5. 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、删除文件

1
file.delete()

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
String*number
  • 参数:
    • 一个字符串作为 * 操作符的左操作数
    • 操作符右侧的数字,表示字符串需要重复的次数。
  • 案例:
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
}

正则表达式规则,可以使用的特殊字符

  1. 有两个特殊的位置字符用于表示一行的开始和结束:^$
  2. 正则表达式也可以包括量词:+ 表示一次或多次,应用于表达式的前一个元素;* 表示零个或多个出现;? 表示零或一次
  3. 元字符 {} 用于匹配前一个字符的特定数量的实例
  4. 在正则表达式中,. 可以表示任何字符。(通配符)
  5. 正则表达式可以包括字符类。一组字符可以作为简单的字符序列,包含在元字符 [] 中,如:[aeiou]。对于字母或数字范围,可以使用 [a-z][a-mA-M] 中的 - 分隔符。字符类的补码由方括号内的前导插入符号表示,如 [^a-z],并表示除指定的字符以外的所有字符。

Groovy 异常处理

同 Java 异常处理机制

Groovy 面向对象

同 Java 面向对象

1
2
3
4
5
6
class/接口
抽象
封装
继承
多态
trait

接口

  1. 接口中不能定义非 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功能
JsonSlurperJsonSlurper 是一个将 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
}

元注解(Meta Annotations)

Groovy 闭包 (Closure)

见下面的 Groovy语法之闭包.md

Groovy 对 Java 的优化

  1. 表达式后面的分号是可选的
  2. 每个类、构造器和方法默认是 public 的
  3. groovy 方法体中的最后一个表达式的值会被作为返回值
  4. groovy 编译器自动加上 getter/setter 方法
  5. 类的属性可以通过点 (.) 来获取,底层是 groovy 自动调用 getter/setter 方法
  6. 用 == 比较两个类的实例,底层调用的是 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 是语言的结构构造,它允许:

  1. 行为的组成
  2. 接口的运行时实现
  3. 和静态类型检查/编译相兼容

它们可以被看作是承载默认实现和状态的接口,用 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 }
  1. 其中 [closureParameters->] 部分是一个可选的以逗号分隔的参数列表
  2. 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()闭包() 或来执行。

  1. 闭包作为一个匿名的代码块,可以像方法那样被调用
  2. 闭包内的代码只有在闭包被调用时才会执行,调用可以像常规方法那样来完成
  3. 可以显式的调用 call 方法调用
  4. 和方法不一样的是,闭包始终有一个返回值

闭包的参数 (Formal parameters in closures)

闭包的参数遵循与常规方法的参数相同的原则 (参数用 , 分隔):

  1. 可选的类型
  2. 名字
  3. 可选的默认值

闭包的显式参数

闭包也可以包含形式参数,以使它们更有用,就像 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. 对于有多个闭包参数的,只要是在参数声明最后的,均可以按上述方式省略
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'

应用闭包

在方法中使用闭包

  1. 闭包也可以作为方法的参数。
    在 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. 匿名内联函数,也称为一个闭包
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 闭包有三个相关的对象:

  1. this 即闭包定义所在的类
  2. owner 即闭包定义所在的对象或闭包
  3. 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 属性,因为相应的属性解析策略:

  1. Closure.OWNER_FIRST,默认策略,首先从 owner 上寻找属性或方法,找不到则在 delegate 上寻找。
  2. Closure.DELEGATE_FIRST,和上面相反。首先从 delegate 上寻找属性或者方法
  3. Closure.OWNER_ONLY,只在 owner 上寻找,delegate 被忽略。
  4. Closure.DELEGATE_ONLY,和上面相反。只在 delegate 上寻找,owner 被忽略。
  5. 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

  1. invokeMethod 方法可以分派所有的方法,包括一个类已经实现了的和未实现的方法;而它实现上面的功能是通过这个类实现 GroovyInterceptable 接口达到的,未实现 GroovyInterceptable 接口时,invokeMethod 和 methodMission 功能一致
  2. methodMissing 方法则只能分派一个类未实现的方法,无论它是否实现了 GroovyInterceptable 接口。

如果我们想让一个方法来管理一个类所有方法的调用,那么我们必须使用 “invokeMethod” 方法;如果我们只想通过一个方法来管理一个类的所有 “missing method”,即不能被分派出去的方法,那么使用 “methodMissing” 方法是比较有效的;当然,”invokeMethod” 方法也能实现 “methodMissing” 方法的功能。

测试

  1. 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)
  1. 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)
  1. 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)
本文由作者按照 CC BY 4.0 进行授权