Markdown版本笔记 我的GitHub首页 我的博客 我的微信 我的邮箱
MyAndroidBlogs baiqiantao baiqiantao bqt20094 baiqiantao@sina.com

Gradle Groovy 基础语法 MD


目录

Groovy 基础

docs文档

api文档

参考1

参考2

为何要学 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来编译和运行。但是我并不想搞那么麻烦,毕竟我们的最终目的只是学习Gradle。

推荐大家通过这种方式来编译和运行Groovy。

在当面目录下创建build.gradle文件,在里面创建一个task,然后在task中编写Groovy代码即可,如下所示:

task(testGroovy).doLast {
println "开始运行自定义task"
test()
} def test() {
println "执行Groovy语法的代码"
System.out.println("执行Java语法的代码!");
}

然后在命令行终端中执行如下命令即可:

gradle testGroovy
> Configure project :app 

> Task :app:testGroovy
开始运行自定义task
执行Groovy语法的代码
执行Java语法的代码! BUILD SUCCESSFUL in 3s
1 actionable task: 1 executed

我们知道,在Android项目中,我们只要更改build.gradle文件一点内容,AS就会提示我们同步:



但是在我们测试 Groovy 时中,我们更改build.gradle文件后可以不必去同步,执行命令时会自动执行你修改后的最新逻辑。

最基本的语法

Groovy中的类和方法默认都是public权限的,所以我们可以省略public关键字,除非我们想使用private。

Groovy中的类型是弱化的,所有的类型都可以动态推断,但是Groovy仍然是强类型的语言,类型不匹配仍然会报错。

Groovy中通过 def 关键字来声明变量和方法

Groovy中很多东西都是可以省略的,比如

  • 语句后面的分号是可以省略的
  • def 和 变量的类型 中的其中之一是可以省略的(不能全部省略)
  • def 和 方法的返回值类型 中的其中之一是可以省略的(不能全部省略)
  • 方法调用时的圆括号是可以省略的
  • 方法声明中的参数类型是可以省略的
  • 方法的最后一句表达式可作为返回值返回,而不需要return关键字

    省略return关键字并不是一个好的习惯,就如同 if else while 后面只有一行语句时可以省略大括号一样,以后如果添加了其他语句,很有可能会导致逻辑错误

def int a = 1; //如果 def 和 类型同时存在,IDE 会提示你"def是不需要的(is unnecessary)"
def String b = "hello world" //省略分号,存在分号时也会提示你 unnecessary
def c = 1 //省略类型 def hello() { //省略方法声明中的返回值类型
println ("hello world");
println "hello groovy" //省略方法调用时的圆括号
return 1;
} def hello(String msg) {
println "hello" + msg //省略方法调用时的圆括号
1; //省略return
} int hello(msg) { //省略方法声明中的参数类型
println msg
return 1 // 这个return不能省略
println "done" //这一行代码是执行不到的,IDE 会提示你 Unreachable statement,但语法没错
}

支持的数据类型

在Groovy中,数据类型有:

  • Java中的基本数据类型
  • Java中的对象
  • Closure(闭包)
  • 加强的List、Map等集合类型
  • 加强的File、Stream等IO类型

类型可以显示声明,也可以用 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

闭包

Groovy中有一种特殊的类型,叫做Closure,翻译过来就是闭包,这是一种类似于C语言中函数指针的东西。

闭包用起来非常方便,在Groovy中,闭包作为一种特殊的数据类型而存在,闭包可以作为方法的参数和返回值,也可以作为一个变量而存在

闭包可以有返回值和参数,当然也可以没有。下面是几个具体的例子:

def test() {
def closure = { String parameters -> //闭包的基本格式
println parameters
}
def closure2 = { a, b -> // 省略了闭包的参数类型
println "a=${a}, b=${b}"
}
def closure3 = { a ->
a + 1 //省略了return
}
def closure4 = { // 省略了闭包的参数声明
println "参数为 ${it}" //如果闭包不指定参数,那么它会有一个隐含的参数 it
} closure("包青天") //包青天
closure2 10086, "包青天" //a=10086, b=包青天
println closure3(1) //2
//println closure3 2 //不允许省略圆括号,会提示:Cannot get property '1' on null object
closure4() //参数为 null
closure4 //不允许省略圆括号,但是并不会报错
closure4 10086 //参数为 10086
}

闭包的一个难题是如何确定闭包的参数(包括参数的个数、参数的类型、参数的意义),尤其当我们调用Groovy的API时,这个时候没有其他办法,只有查询Groovy的文档才能知道。

List和Map

Groovy加强了Java中的集合类,比如List、Map、Set等。

基本使用如下:

def emptyList = []
def list = [10086, "hello", true]
list[1] = "world"
assert list[1] == "world"
println list[0] //10086
list << 5 //相当于 add()
assert 5 in list // 调用包含方法
println list //[10086, world, true, 5]
def range = 1..5
assert 2 in range
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 //{id=10086, name=哈哈, age=29}

可以看到,通过Groovy来操作List和Map显然比Java简单的多。

上面有一个看起来很奇怪的操作符<<,其实这并没有什么大不了,<<表示向List中添加新元素的意思,这一点从 List文档 当也能查到。

public List leftShift(Object value)
  • Overloads the left shift operator to provide an easy way to append objects to a List. 重载左移位运算符,以提供将对象append到List的简单方法。
  • Parameters: value - an Object to be added to the List.
  • Returns: same List, after the value was added to it.

实际上,这个运算符是大量使用的,并且当你用 leftShift 方法时 IDE 也会提示你让你使用左移位运算符<<替换:

def list = [1, 2]
list.leftShift 3
assert list == [1, 2, 3]
list << 4
println list //[1, 2, 3, 4]

闭包的参数

这里借助Map再讲述下如何确定闭包的参数。比如我们想遍历一个Map,我们想采用Groovy的方式,通过查看文档,发现它有如下两个方法,看起来和遍历有关:

  • Map each(Closure closure):Allows a Map to be iterated through using a closure.
  • Map eachWithIndex(Closure closure):Allows a Map to be iterated through using a closure.

可以发现,这两个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 the Map.Entry otherwise if the closure takes two parameters then it will be passed the key and the value.
  • In general, the order in which the map contents are processed cannot be guaranteed(通常无法保证处理元素的顺序). In practise(在实践中), specialized forms of Map(特殊形式的Map), e.g. a TreeMap will have its contents processed according to the natural ordering(自然顺序) of the map.
def result = ""
[a:1, b:3].each { key, value -> result += "$key$value" } //两个参数
assert result == "a1b3"
def result = ""
[a:1, b:3].each { entry -> result += entry } //一个参数
assert result == "a=1b=3" [a: 1, b: 3].each { println "[${it.key} : ${it.value}]" } //一个隐含的参数 it,key 和 value 是属性名

试想一下,如果你不知道查文档,你又怎么知道each方法如何使用呢?光靠从网上搜,API文档中那么多接口,搜的过来吗?记得住吗?

加强的IO

在Groovy中,文件访问要比Java简单的多,不管是普通文件还是xml文件。怎么使用呢?查来 File文档

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.
  • Parameters: closure - a closure (arg 1 is line, optional arg 2 is line number starting at line 1)
  • Returns: the last value returned by the closure

可以看到,eachLine方法也是支持1个或2个参数的,这两个参数分别是什么意思,就需要我们学会读文档了,一味地从网上搜例子,多累啊,而且很难彻底掌握:

def file = new File("a.txt")
file.eachLine { line, lineNo ->
println "${lineNo} ${line}" //行号,内容
} file.eachLine { line ->
println "${line}" //内容
}

除了eachLine,File还提供了很多Java所没有的方法,大家需要浏览下大概有哪些方法,然后需要用的时候再去查就行了,这就是学习Groovy的正道。

访问xml文件

Groovy访问xml有两个类:XmlParserXmlSlurper,二者几乎一样,在性能上有细微的差别,不过这对于本文不重要。

groovy.util.XmlParserAPI文档

文档中的案例:

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'] }

更多的细节查文档即可。

其他的一些语法特性

Getter和Setter

当你在Groovy中创建一个beans的时候,通常我们称为POGOS(Plain Old Groovy Objects),Groovy会自动帮我们创建getter/setter方法。

当你对getter/setter方法有特殊要求,你尽可提供自己的方法,Groovy默认的getter/setter方法会被替换。

构造器

有一个bean

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("包青天")
}

判断是否为真

所有类型都能转成布尔值,比如nullvoid相当于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,如果你真想捕获everything,你必须明确的标明你想捕获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 时会继续向下判断
case "bar": result += "bar"
break
case [4, 5, 6]: result = "list" //匹配集合中的元素
break
case 12..30: result = "range" //匹配某个范围内的元素
break
case Integer: result = "integer" //匹配Integer类型
break
case { it > 3 }: result = "number > 3" //匹配表达式
break
case Number: result = "number" //匹配Number类型
break
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.Listjava.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

Gradle Groovy 基础语法 MD的更多相关文章

  1. Groovy系列(3)- Groovy基础语法

    Groovy基础语法 动态类型 Groovy定义变量时:可以用Groovy风格的def声明,不指定类型:也可以兼容Java风格,指定变量类型:甚至还可以省略def或类型 def t1 = 't1' S ...

  2. Groovy基础语法

    Groovy 基础语法 变量定义 1.支持动态类型,使用def关键字定义变量 // Java中定义变量的方式 int age = 18; String name = "张三"; / ...

  3. Gradle+Groovy基础篇

    在Java项目中,有两个主要的构建系统:Gradle和Maven.构建系统主要管理潜在的复杂依赖关系并正确编译项目.还可以将已编译的项目以及所有资源和源文件打包到.war或.jar文件中.对于简单的构 ...

  4. -Shell 教程 Bash 脚本 基础语法 MD

    目录 目录 Shell 简介 Shell 脚本 Shell 环境 第一个shell脚本 Shell 变量 定义变量 使用变量 只读变量 删除变量 Shell 字符串 单引号 双引号 字符串基本操作 S ...

  5. groovy常用语法及实战

    groovy语言简介 一种基于JVM的敏捷开发语言,作为编程语言可编译成java字节码,也可以作为脚本语言解释执行. 结合了Python.Ruby和Smalltalk的许多强大的特性 支持面向对象编程 ...

  6. 【Gradle】Groovy基础

    Groovy基础 Groovy是基于JVM虚拟机的一种动态语言.每个Gradle的build脚本文件都是一个Groovy脚本文件. 字符串 在Groovy中,分号不是必需的.在Groovy中,单引号和 ...

  7. .md(markdown)基础语法

    markdown基础语法笔记,方便翻看. 1. 标题 标题有3中写法,可混写: (1)# ***,前面#,后面文字,注意,#与文字间有空格,1-6个#分别表示h1-h6,h1.h2下有横线 (2)== ...

  8. gradle使用基础

    说明 介绍gradle使用基础,gradle基础脚本结构和常规使用方法,以及一个简单的gradle示例.主要是为了简单的介绍gradle使用. gradle环境配置 gradle可以通过两种方式运行g ...

  9. Java基础语法

    java基础学习总结——基础语法1 一.标识符

随机推荐

  1. BZOJ4161 常系数齐次线性递推

    问了数竞的毛毛搞了一番也没太明白,好在代码蛮好写先记下吧. #include<bits/stdc++.h> using namespace std; ,mod=1e9+; int n,k, ...

  2. Stf-windows版本

    Stf-windows Stf 原项目地址:https://github.com/openstf/stf . 介绍 用于Web端设备远程管理 系统支持 支持Android版本2.3.3 (SDK10) ...

  3. 【Codeforces666E】Forensic Examination 后缀自动机 + 线段树合并

    E. Forensic Examination time limit per test:6 seconds memory limit per test:768 megabytes input:stan ...

  4. Xtreme9.0 - Pattern 3 KMP

    Pattern 3 题目连接: https://www.hackerrank.com/contests/ieeextreme-challenges/challenges/car-spark Descr ...

  5. Booting LPC-Link2, Updating LPCXpresso firmware

    Booting LPC-Link2 The recommended way to use LPC-Link2 with the LPCXpresso IDE is to boot and soft l ...

  6. VMware Workstation Pro 12 桥接联网(物理主机:Windows 7,虚拟机:CentOS 6.8)

    物理主机:Windows 7,虚拟机:CentOS 6.8 1.设置虚拟机的 默认路径:编辑 -> 首选项 -> 设置“虚拟机的默认位置” 2.设置 虚拟网络:编辑 -> 虚拟网络编 ...

  7. java系列之 原生数据类型

    在我看来,java里面里面除了原生类型不是对象,其他的都是对象.但java是面向对象的语言,很多地方还要要操作对象,所以java会自动把原生类型转为对应的包装类型.这个过程叫自动装箱.有装箱就有拆箱, ...

  8. 基于设备树的TQ2440的中断(1)

    作者 姓名:彭东林 E-mail:pengdonglin137@163.com QQ:405728433 平台 板子:TQ2440 内核:Linux-4.9 u-boot: 2015.04 工具链: ...

  9. AngularJS中Directive间交互实现合成

    假设需要烹饪一道菜肴,有3种原料,可以同时使用所有的3种原料,可以使用其中2种,也可以使用其中1种. 如果以Directive的写法,大致是:<bread material1 material2 ...

  10. maven生命周期理解

    你可以仅仅调用clean来清理工作目录,仅仅调用site来生成站点.当然你也可以直接运行 mvn clean install site 运行所有这三套生命周期. 知道了每套生命周期的大概用途和相互关系 ...