Groovy Gradle JVM 基础语法
目录
Groovy Gradle JVM 基础语法
Groovy 简介
为何要学 Groovy
为何要学 Groovy
- Gradle 是目前 Android 主流的构建工具,不管你是通过命令行还是通过 AndroidStudio 来 build,最终都是通过 Gradle 来实现的。所以学习 Gradle 非常重要。
- 目前国内对 Android 领域的探索已经越来越深,不少技术领域如
插件化、热修复、构建系统
等都对 Gradle 有迫切的需求,不懂 Gradle 将无法完成上述事情。所以 Gradle 必须要学习。
Gradle 不单单是一个配置脚本,它的背后是几门语言:
- Groovy Language(主)
- Gradle DSL
- Android DSL
DSL的全称是
Domain Specific Language
,即领域特定语言
,或者直接翻译成特定领域的语言
,再直接点,其实就是这个语言不通用,只能用于特定的某个领域,俗称小语言
。因此,DSL 也是一门语言
。
实际上,Gradle 脚本大多都是使用 groovy 语言编写的。
Groovy 是一门 jvm
语言,功能比较强大,细节也很多,全部学习的话比较耗时,对我们来说收益较小,并且玩转 Gradle 并不需要学习 Groovy 的全部细节,所以其实我们只需要学一些 Groovy 基础语法与 API 即可。
为何要使用 Groovy
Groovy 是一种基于JVM
的敏捷开发语言,它结合了众多脚本语言
的强大的特性,由于同时又能与Java代码很好的结合
。一句话:既有面向对象的特性又有纯粹的脚本语言的特性
。
由于 Groovy 运行在 JVM 上,因此也可以使用 Java 语言编写的组件。
简单来说,Groovy 提供了更加灵活简单的语法
,大量的语法糖
以及闭包
特性可以让你用更少的代码来实现和Java同样的功能
。
如何编译运行 Groovy
Groovy 是一门 jvm 语言,它最终是要编译成class文件
然后在 jvm 上执行,所以Java语言的特性Groovy都支持
,Groovy 支持 99% 的 java 语法,我们完全可以在 Groovy 代码中直接粘贴 java 代码
。
安装 Groovy SDK 来编译和运行
- 下载 Groovy SDK,并解压到自己的存放路径
- 配置的环境变量
- 系统变量:
GROOVY_HOME
C:\Android\groovy\groovy-3.0.4
- 系统变量 -- Path:
%GROOVY_HOME%\bin
- 系统变量:
- 命令行中输入
groovy -v
判断配置是否成功 - 命令中输入
groovyConsole
启动 GroovySDK 自带的编辑器,或者双击运行bin\groovyConsole.bat
- 编写 Groovy 代码,右键或
CTRL + R
编译运行 - 也可以
File -> Save
或CTRL + S
保存为.groovy
格式的文件(保存为UTF-8
格式),然后在命令行中通过groovy Test.groovy
运行
在 AS 中编译运行的几种方式
通过 AndroidStudio 创建的 Android 项目,可以在build.gradle
中新建任务后,通过 gradle 编译运行 Groovy 代码。
双击运行指定的 task
- 在 Project 的
build.gradle
新建一个task
,然后在 task 中编写 Groovy 代码,例如:
task(_testGroovy).doLast {
println "开始运行自定义task"
test("helloTask")
}
def test(s) {
println s
System.out.println("执行Java语法的代码!");
}
- 然后在
Gradle - Tasks - other
里面找到对应的 Task,双击运行
在命令行中通过 gradle 命令运行
配置 gradle 环境变量后,可以在命令行终端中执行对应的 Task。
- 配置的环境变量,个人不同版本的 gradle 的安装目录在
C:\Android\AS_gradle\wrapper\dists
- 系统变量:
GRADLE_HOME
C:\Android\AS_gradle\wrapper\dists\gradle-4.10.1-all\455itskqi2qtf0v2sja68alqd\gradle-4.10.1
- 系统变量 -- Path:
%GRADLE_HOME%\bin
- 系统变量:
- 重启 AS,在命令行
Terminal
中输入gradle -v
判断配置是否成功 - 然后在命令行终端中执行如下命令:
gradle _testGroovy
Tips
我们知道,在Android项目中,我们只要更改build.gradle
文件一点内容,AS就会提示我们同步:
但是在我们测试 Groovy 时中,我们更改build.gradle
文件后可以不必去同步,执行命令时会自动执行你修改后的最新逻辑。
在 AS 的 Groovy Console 中编译运行
单击 AS 菜单 Tools -> Groovy Consoles,会进入一个 Groovy Consoles 界面,可以在里面编译运行 Groovy 代码,可以运行Java类型也可以是脚本类型的代码
Groovy 的语法
最基本的语法
- Groovy中的类和方法默认都是
public
权限的,所以我们可以省略public
关键字,除非我们想使用private
。 - Groovy中的
类型
是弱化的,所有的类型都可以动态推断
,但是Groovy仍然是强类型的语言
,类型不匹配仍然会报错。 - Groovy中通过
def
关键字来声明变量和方法
。
Groovy中很多东西都是可以省略的,比如
- 语句后面的
分号
是可以省略的 def
和变量的类型
中的其中之一是可以省略的(不能全部省略)def
和方法的返回值类型
中的其中之一是可以省略的(不能全部省略)方法调用时的圆括号
是可以省略的方法声明中的参数类型
是可以省略的- 方法的最后一句表达式可作为返回值返回,而不需要
return
关键字。省略 return 关键字并不是一个好的习惯
def s = "hello world"; //存在分号时会提示你 Semicolon is unnecessary
def int a = 1 //如果 def 和 类型同时存在,IDE 会提示你 def is unnecessary
def c = 1 //省略类型
def hello(String msg) { //省略方法声明中的返回值类型
println "hello" + msg //省略方法调用时的圆括号
1 //省略return,不建议
}
int hello(msg) { //省略方法声明中的参数类型
return 2 // 这个return不能省略
println msg//IDE 会提示你 Unreachable statement,但语法没错
}
支持的数据类型
在Groovy中,数据类型有:
- Java中的基本数据类型和对象
- 加强的List、Map、File、Stream等集合和 IO 类型
- 闭包
Closure
类型可以显示声明,也可以用 def 来声明,用 def 声明的类型 Groovy 将会进行类型推断。
基本数据类型和对象和 Java 中的一致,只不过在 Gradle 中,对象默认的修饰符为public
。
String
String的特色在于字符串的拼接
def a = 1
def b = "hello"
def c = "a=${a}, b=${b}"
println c //a=1, b=hello
闭包 Closure
在 Groovy 中,闭包是一段匿名的代码段
,它可以有参数,返回值
,并且能够赋值给一个变量
。闭包中使用的变量可以是在闭包外部定义的,也可以是在闭包内部定义的。
- 可以作为
方法的参数和返回值
- 可以作为一个
变量
而存在 - 可以有
返回值和参数
,也可以没有
def test() {
def closure = { String params -> //闭包的基本格式
println params
}
def closure2 = { a, b -> // 省略了闭包的参数类型声明,省略返回值声明
println "a=${a}, b=${b}"
a //省略return
}
closure("包青天") //包青天
closure2 10086, "包青天" //省略小括号
}
test() //主动调用方法才会执行
def test() {
def closure3 = { a ->
return a + 1
}
def closure4 = { // 省略了闭包的参数声明
println "参数为 ${it}" //如果闭包不指定参数,那么它会有一个隐含的参数 it
}
println closure3(1) //2
println closure3("1") //11
//println closure3 2 //不允许省略圆括号,否则运行时会提示:Cannot get property '2' on null object
closure4() //参数为 null
closure4 //省略圆括号时没有任何打印,但是执行时并不会报错
closure4 10086 //参数为 10086
}
test() //主动调用方法才会执行
闭包的一个难题是如何确定闭包的参数
(包括参数的个数
、参数的类型
、参数的意义
),尤其当我们调用 Groovy 的 API 时,这个时候没有其他办法,只有查询 Groovy 的文档才能知道。
加强的集合
Groovy 加强了 Java 中的集合类,比如 List、Map、Set 等。
基本使用如下:
def emptyList = []
def list = [10086, "hello", true]
list[1] = "world"
assert list[1] == "world" //assert 后面的语句是true时才继续向下执行,否则异常退出
println list[0] + "-" + list[1] //10086-world
list << 5 //相当于 add(5)
if (5 in list) {// 是否包含
println list //[10086, world, true, 5]
}
def range = 1..5
println range //1..5
println range.size() //5
def emptyMap = [:]
def map = ["id": 1, "name": "包青天"]
map << [age: 29] //添加元素
map["id"] = 10086 //访问元素方式一
map.name = "哈哈" //访问元素方式二,这种方式最简单
println map.size() + "-" + map //3-{id=10086, name=哈哈, age=29}
println map.id + "-" + map["name"] + "-" + map.get("age")
可以看到,通过 Groovy 来操作 List 和 Map 显然比 Java 简单的多。
加强的 IO
在Groovy中,文件访问要比Java简单的多
def file = new File("settings.gradle")
file.eachLine { line, lineNo ->
println "${lineNo} ${line}" //行号,内容
}
file.eachLine { line ->
println "${line}" //内容
}
Groovy访问xml有两个类:XmlParser 和XmlSlurper
,二者几乎一样,在性能上有细微的差别要。
def xml = '<root><one a1="uno!"/><two>Some text!</two></root>'
//或者 def xml = new XmlParser().parse(new File("filePath.xml"))
def rootNode = new XmlParser().parseText(xml) //根节点
assert rootNode.name() == 'root' //根节点的名称
assert rootNode.one[0].@a1 == 'uno!' //根节点中的子节点 one 的 a1 属性的值
assert rootNode.two.text() == 'Some text!' //根节点中的子节点 two 的内容
rootNode.children().each { assert it.name() in ['one','two'] }
如何查看文档
File 类的 eachLine 方法
2、在找到相应的方法,例如eachLine
方法
public Object eachLine(Closure closure)
- Iterates through this file line by line.
- Each line is passed to the given 1 or 2 arg
closure
. - The file is read using a
reader
which is closed before this method returns.
- Each line is passed to the given 1 or 2 arg
- Parameters: closure - a closure
arg 1
is line,第一个参数表示当前行的内容- optional
arg 2
is line number starting at line 1,可选的第二个参数表示从1开始的行号
- Returns: the last value returned by the closure
3、然后就可以尝试着使用了
new File("settings.gradle").eachLine { line ->
println "${line}
}
4、在 AS 中点击此方法后的声明,会发现是以下形式:
public class ResourceGroovyMethods extends DefaultGroovyMethodsSupport {
public static <T> T eachLine(File self, @ClosureParams(value = FromString.class,options = {"String", "String,Integer"}) Closure<T> closure) throws IOException {
return eachLine((File)self, 1, closure);
}
}
public static <T> T
:表明这是一个 groovy 扩展出来的一个静态方法File self
:表明第一个参数是自己,也就是 file,实际使用中都不需要这个参数@ClosureParams(..)
:用来声明闭包的参数个数、类型的value = FromString.class
:用于在编译时推断闭包的参数类型(IDE会用来提示)options = {...}
:推断类型时,传递给提示的一组选项,也可供resolver使用"String"
:表明第一个参数是String类型"String,Integer"
:表明第二个参数是String类型,但是可以转换为Integer类型
Closure<T> closure
:表明需要一个闭包类型的参数
new File("settings.gradle").eachLine { line -> println "$line" }
new File("settings.gradle").eachLine { line, i ->
int num = Integer.parseInt("$i") + 1
println "${line}----$num"
}
集合的左移位运算符 <<
集合的左移位运算符<<
表示向集合中添加新元素,在 AS 中点击这个操作符可以看到,实际上其调用的是一个叫leftShift
的方法:
public static <T> List<T> leftShift(List<T> self, T value) {
return (List)leftShift((Collection)self, (Object)value);
}
public static <T> Collection<T> leftShift(Collection<T> self, T value) {
self.add(value);
return self;
}
//接口 Collection 中定义的 add 方法
boolean add(E e);
从 List文档 当中可以查到此方法的介绍:
- Overloads重载 the
left shift operator
左移位运算符 to provide an easy way to append objects to a List. - Parameters: value - an Object to be added to the List.
- Returns: same List, after the value was added to it.
实际上,这个运算符是大量使用的,并且当你用 leftShift
方法时 IDE 也会提示你让你使用左移位运算符<<
替换。
map 的 each 方法
each 方法的声明:
public Map each(Closure closure)
- Allows a Map to be
iterated
through using a closure.- If the closure takes
one parameter
then it will be passed theMap.Entry
- if the closure takes
two parameters
then it will be passedthe key and the value
.
- If the closure takes
- In general, the order in which the map contents are processed cannot be guaranteed 通常无法保证处理元素的顺序.
- In practise在实践中, specialized forms特殊形式的 of Map, e.g. a
TreeMap
will have its contents processed处理其内容 according to thenatural ordering
自然顺序 of the map.
[a: 1, b: 3].each { kv -> println "一个参数 $kv" }
[a: 1, b: 3].each { key, value -> println "二个参数 ${key}=$value" }
[a: 1, b: 3].each { println "隐含参数 ${it.key}=${it.value}" } //隐含参数 it,key 和 value 是属性名
其他的一些语法特性
Getter和Setter
当你在 Groovy 中创建一个 beans 的时候,通常我们称为POGOS(Plain Old Groovy Objects)
,Groovy 会自动帮我们创建getter/setter
方法。
当你对getter/setter
方法有特殊要求,你尽可提供自己的方法,Groovy 默认的getter/setter
方法会被替换。
构造器
class Server {
String name
Cluster cluster
}
初始化一个实例的时候你可能会这样写:
def server = new Server()
server.name = "Obelix"
server.cluster = aCluster
其实你可以用带命名的参数的默认构造器,会大大减少代码量:
def server = new Server(name: "Obelix", cluster: aCluster)
Class类型
在Groovy中Class类型的.class
后缀不是必须的,比如:
def func(Class clazz) {
println clazz
}
func(File.class) //class java.io.File
func(File) //class java.io.File
使用with()操作符
当更新一个实例的时候,你可以使用with()来省略相同的前缀,比如:
Book book = new Book()
book.with {
id = 1 //等价于 book.id = 1
name = "包青天"
start(10086)
stop("包青天")
}
判断是否为真
所有类型都能转成布尔值
,比如null
和void
相当于0
或者相当于false
,其他则相当于true
,所以:
if (name) {}
//等价于
if (name != null && name.length > 0) {}
在 Groovy 中可以在类中添加asBoolean()
方法来自定义是否为真
。
简洁的三元表达式
在Groovy中,三元表达式可以更加简洁,比如:
def result = name ?: ""
//等价于
def result = name != null ? name : ""
捕获任何异常
如果你实在不想关心try
块里抛出何种异常,你可以简单的捕获所有异常,并且可以省略异常类型
:
try {
// ...
} catch (any) { //可以省略异常类型
// something bad happens
}
这里的any并不包括Throwable
,如果你想捕获Throwable,你必须明确的表明你想捕获Throwable
简洁的非空判断
在 java 中,你要获取某个对象的值必须要检查是否为null,这就造成了大量的if
语句;在 Groovy 中,非空判断可以用?.
表达式,比如:
println order?.customer?.address
//等价于
if (order != null) {
if (order.getCustomer() != null) {
if (order.getCustomer().getAddress() != null) {
System.out.println(order.getCustomer().getAddress());
}
}
}
使用断言
在 Groovy 中,可以使用assert
来设置断言,当断言的条件为false时,程序将会抛出异常:
def check(String name) {
assert name // 检查方法传入的参数是否为空,name non-null and non-empty according to Groovy Truth
assert name?.size() > 3
}
== 和 equals
- Groovy里的
is()
方法等同于Java里的==
。 - Groovy中的
==
是更智能的equals()
,比较两个类的时候,你应该使用a.is(b)
而不是==
- Groovy中的
==
可以自动避免 NullPointerException 异常
status == "包青天"
//等价于Java中的
status != null && status.equals("包青天")
switch方法
在 Groovy 中,switch 方法变得更加灵活,可以同时支持更多的参数类型:
def x = null
def result = ""
switch (x) {
case "foo": result = "found foo" //没有 break 时会继续向下判断
break
case "bar": result += "bar"
case [4, 5, 6]: result = "list" //匹配集合中的元素
case 12..30: result = "range" //匹配某个范围内的元素
case Integer: result = "integer" //匹配Integer类型
case { it > 3 }: result = "number > 3" //匹配表达式
case Number: result = "number" //匹配Number类型
default: result = "default"
}
println result
字符串分行
Java 中,字符串过长需要换行时我们一般会这样写:
throw new PluginException("Failed to execute command list-applications:" +
" The group with name " +
parameterMap.groupname[0] +
" is not compatible group of type " +
SERVER_TYPE_NAME)
Groovy中你可以用 \
字符,而不需要添加一堆的双引号:
throw new PluginException("Failed to execute command list-applications: \
The group with name ${parameterMap.groupname[0]} \
is not compatible group of type ${SERVER_TYPE_NAME}")
或者使用多行字符串"""
:
throw new PluginException("""Failed to execute command list-applications:
The group with name ${parameterMap.groupname[0]}
is not compatible group of type ${SERVER_TYPE_NAME)}""")
Groovy中,单引号
引起来的字符串是java字符串,不能使用占位符来替换变量,双引号
引起的字符串则是java字符串或者Groovy字符串。
Import 别名
在java中使用两个类名相同但包名不同的两个类,像java.util.List
和java.wt.List
,你必须使用完整的包名才能区分。Groovy中则可以使用import别名:
import java.util.List as jurist //使用别名
import java.awt.List as aList
import java.awt.WindowConstants as WC
import static pkg.SomeClass.foo //静态引入方法
2019-1-12
本文来自博客园,作者:白乾涛,转载请注明原文链接:https://www.cnblogs.com/baiqiantao/p/10261560.html