Theme Preview

Hue:

You are using an outdated browser that does not support OKLCH colors. The color setting will not take effect.

Gradle之旅(1):Groovy入门指南

2737 字

一、前言

对于任何软件来说,构建自动化测试方案是非常有用,Gradle 作为基于 JVM 的构建工具,兼顾灵活性和高性能,有着约定优于配置的方法、强大的依赖管理,它的构建脚本使用 Groovy 或 Kotlin 编写,是Android的官方构建工具,而在本文要探讨的就是 Groovy 语言。

二、Groovy 简介

Groovy 是 Apache 旗下的一种基于 Jvm 的面向对象编程语言,既可以用于面向对象编程,也可以用作纯粹的脚本语言。它在兼容Java语法的同时,借鉴了 Ruby、Python 等语言的特性,比如动态类型转换、闭包和元编程支持等。同时,运行在 JVM 上也意味着它也可以使用Java语言编写的库。比如在写 Groovy 的时候忘记了语法可以直接按 Java 的语法继续写,也可以在 Java 中调用 Groovy 脚本。比起Java,Groovy 语法更加的灵活和简洁,同样的功能 Groovy 代码量更少。这就使得 Groovy 极其适合编写 Java 代码的测试脚本。

二、安装环境

Groovy官网 上下载二进制的安装包,这里以 4.0.21 版本为例:

1、 解压并移动到指定目录下

mv groovy-4.0.21/ ~/source/bin/

2、配置环境变量

$ vim ~/.bashrc

# 配置环境变量
export PATH=$PATH:$HOME/source/bin/groovy-4.0.21/bin

# 使配置生效
$ source ~/.bashrc

3、校验

$ which groovy

/home/zsk/source/bin/groovy-4.0.21/bin/groovy

至此环境安装完成

三、从 Hello World 开始

首先要说明的是在 Groovy 中完全兼容 Java 语法,因此在任何要使用 Groovy 的地方都可以直接使用 Java 来实现!,因此在本文中主要介绍 Groovy 有别于 Java 的地方。

3.1 输出特性

先来看一下 Groovy 输出方式:

1. print xxx                                  //同一行输出
2. println xxx //换行输出
3. println("My name is :"+${name}) //插值形式
4. printf("%d plus %d is %d\n",a,b,(a+b)) //格式化输出,注意是 printf 而不是 println

Groovy 中输出语句不依靠类和函数,即可以不定义在类和函数内部。这里借由 Hello World 的两种示例来展示 Groovy 的特性:

1、方式一:

新建 HelloWorld.groovy 文件,写入一下代码:

static main(args){
println "Hello World!"
}

2、方式二:

println "Hello World!"

使用 groovy 来执行这个脚本,会得到相同的结果:

$ groovy HelloWorld.groovy 

Hello World!

3.2 变量及类型

3.2.1 变量类型

Groovy 中数据类型在完全支持 Java 的同时又扩展了其他的几种类型如下:

类型 类型
allprojects 包含此项目及其子项目的集合
ant 此项目的AntBuilder,可以在构建文件中使用它来执行 ant 任务
long BigInteger
float double
BigDecimal boolean

3.2.2 Groovy 中变量声明方式:

  • 显式声明: 明确变量的数据类型,如: int age = 18;
  • 隐式声明: 不明变量的数据类型,如: def age = 18;

Groovy 中用 def 关键字来定义变量,可以不指定变量的类型,groovy 会根据变量的值自动赋予变量类型,默认访问修饰符是 public :

def number = 1
print(num1 instanceof Integer)
// 输出:true

def number = 111111111111;
print(num2 instanceof Long)
// 输出:true

3.2.3 集合类型

Groovy 没有定义自己的集合类,它在 Java 集合类的基础上进行了增强和简化。Groovy 的 List 对应Java中的 List 接口,默认的实现类为 Java 中的 ArrayList:

def number = [1, 2, 3]         
assert number instanceof List
def linkedList = [1, 2, 3] as LinkedList
assert linkedList instanceof java.util.LinkedList

可以使用 as 操作符来显式指定 List 的实现类为 java.util.LinkedList。
获取元素同样要比 Java 要简洁些,使用 [] 来获取 List 中具有正索引或负索引的元素,使用 remove 方法来删除元素。

def number  = [1, 2, 3, 4]   
assert number [1] == 2
assert number [-1] == 4 // 注释 ①

number << 5 // 注释 ②
assert number [4] == 5
assert number [-1] == 5

number.remove(2)

注释 ①处的索引 -1 是列表末尾的第一个元素。注释 ② 处使用 << 运算符在列表末尾追加一个元素。

3.2.4 Map 类型

Groovy 的 map 对象就是 Java 中的 LinkedHashMap,使用 [] 定义一个 map,map 中的 key、value 对使用逗号分隔。可以使用 [] 取出 key 对应的 value,也能使用 . 取出 value,添加 map 的键值对,使用 put 方法或者 <<

def map = [red: 1, green:2]
assert map['red'] == 1
assert map.green == 2

map << [block: 4, blue :7]
// 或
map.put("yellow", 8)

Map 的键关联问题:

def key = 'red'
def map = [key:1] // 注释 ①
assert map.containsKey('key')
map = [(key): 1] // 注释 ②
assert map.containsKey('red')

注释 ① 处的键值是 key 这个字符串,而不是key变量的值 red 。如果想要以 key 变量的值为键值,需要像注释 ② 处一样使用 (key),用来告诉解析器我们传递的是一个变量,而不是定义一个字符串键值。

3.3 语句

3.3.1 断言

//使用断言时,应包含一条消息。此消息可以帮助其他人维护和理解你的代码,理清你的意图。
assert 1==2:"One isn't Two"

3.3.2 for循环

Groovy 支持 Java 的 for(int i=0;i<N;i++) 和 for(int i : array) 形式的循环语句,另外还支持 for in loop 形式,支持遍历范围、列表、Map、数组和字符串等多种类型

//遍历范围
def x = 0
for ( i in 0..3 ) {
x += i
}
assert x == 6
//遍历列表
def x = 0
for ( i in [0, 1, 2, 3] ) {
x += i
}
assert x == 6
//遍历Map中的值
def map = ['a':1, 'b':2, 'c':3]
x = 0
for ( v in map.values() ) {
x += v
}
assert x == 6

3.3.3 switch语句

Groovy 中的 Switch 在兼容 Java 原有的方式上,又扩展了新的特性,即可以处理更多类型的表达式,如字符串、列表、范围、Integer等等

def x = 16
def result = ""

switch ( x ) {
case "ok":
result = "found ok"
case [1, 2, 4, 'list']:
result = "list"
break
case 10..19:
result = "range"
break
case Integer:
result = "integer"
break
default:
result = "default"
}
assert result == "range"

3.3.4 字符串

  1. 普通字符串 String
// 单引号字符串
def name = '我有一只小毛驴'
// 双引号字符串,可以插值
println "hello ${name}"
println "hello $name"

//三引号字符串,可以保留文本的换行和缩进格式,不支持插值
def name = '''你好啊
汤姆猫
你的家在哪里?'''
  1. 可变字符串 GString

String 是不可变的,GString 却是可变的,GString 和 String 即使有相同的字面量,它们的 hashCodes 的值也可能不同,因此应该避免使用使用 GString 作为 Map 的 key,简而言之:当双引号字符串中包含插值表达式时,字符串类型为GString

3.4 闭包

Groovy 中的闭包是一个开放的、匿名的、可以接受参数和返回值的代码块,同样闭包可以作为对象、参数和返回值给其他地方使用,闭包的定义遵循以下语法:

{ [ closureParameters -> ] statements }

参数列表部分是可选的,如果闭包只有一个参数,参数名是可选的,Groovy 会隐式指定 it 作为参数名,示例如下:

// 一个参数
{ println it } //使用隐式参数it的闭包
{ it -> println it } //it是一个显示参数
//多个参数
{ String a, String b ->
println "${a} is a ${b}"
}

闭包调用:

def code = { 123 }
assert code() == 123 //闭包当做方法调用
assert code.call() == 123 //显示调用call方法
def isOddNumber = { int i -> i%2 != 0 }
assert isOddNumber(3) == true //调用带参数的闭包

//复杂调用
def addX = { int x ->
return { int y ->
println "x is $x, y is $y"
return x + y
}
}
def add1 = addX(1)
println add1(2)

//闭包addX会返回另外一个闭包。之后我们直接调用闭包addX,得到了闭包add1,再直接调用闭包add1进行输出。上面的代码会输出
//x is 1, y is 2
//3

3.5 方法

在兼容 Java 原有的定义方法的基础上,扩展了使用 def 关键字来定义方法,这种形式下方法可以接收任意数量的参数,这些参数可以不申明类型,如果不提供可见性修饰符,则该方法为 public。

def add(int a,int b) { 
println a+b //注释 ①
}

def minus(a,b) {
println a-b //注释 ②
}

int minus(a,b) {
return a-b //注释 ③
}

int minus(a,b) {
a-b //注释 ④
}

//调用
minus 1,2 //注释 ⑤

从上面两段代码中可以发现 Groovy 中有很多省略的地方:

  • 语句后面的分号可以省略。
  • 方法的括号可以省略,比如注释 ⑤ 处。
  • 参数类型可以省略,比如注释 ② 处。
  • return可以省略掉,比如注释 ④处。

3.6 类

Groovy 类与 Java 类有以下的区别:

  • 默认类的修饰符为 public。
  • 没有可见性修饰符的字段会自动生成对应的 setter 和 getter 方法。
  • 类不需要与它的源文件有相同的名称,但还是建议采用相同的名称。
class Person {                       
String name
Integer age =10
def increaseAge(Integer years) {
this.age += years
}
}


//调用
def p = new Person()
p.name = "zsk"
p.increaseAge 5
println p.age

//上面代码中的 p.name = "zsk" 实际上调用了set方法

3.7 元编程

元对象编程可用于动态调用方法,即可以即时创建类和方法,如下 Student 类,使用 MetaClass 来实现一些有意思的事:


//修改私有变量的值
class Student {
private String name = "tom";
public String getName() {
return this.name;
}
}
Student student = new Student()
println student.getName()
student.metaClass.setAttribute(student, 'name', 'zsk')
println student.getName()


//添加方法
class Student {
def name
}
Student student = new Student(name: 'Kitty')
student.metaClass.getName = { println "Meow" }
student.getName()

3.8 IO操作

  1. 文件读取
def filePath = "D:/Android/name.txt"
def file = new File(filePath) ;
file.eachLine {
println it
}

//或者
def filePath = "D:/Android/name.txt"
def file = new File(filePath) ;
println file.text
  1. 文件写入
def filePath = "D:/Android/name.txt"
def file = new File(filePath);

file.withPrintWriter {
it.println("111")
it.println("222")
}

3.9 其他

  1. 安全取值
println school?.student?.name
  1. with 操作符

class Person {
String name
Integer age
String sex
}

Person p = new Person()
p.with {
name = "杨影枫"
age= 19
sex= "男"
}
println p.name
  1. asType 数据类型转换
String a = '23'
int b = a as int
def c = a.asType(Integer)
assert c instanceof java.lang.Integer

3.10 语法糖

语法糖。所谓语法糖就相当于汉语里的成语。即,用更简练的言语表达较复杂的含义。在得到广泛接受的情况之下,可以提升交流的效率。上面所介绍的各种简单易用的书写形式就是语法糖。

3.11 手册

参考手册

//