Gradle Groovy 基础语法 MD

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

Gradle Groovy 基础语法 MDhtml


目录

Groovy 基础

docs文档
api文档
参考1
参考2java

为什么要学 Groovy

Gradle是目前Android主流的构建工具,无论你是经过命令行仍是经过AndroidStudio来build,最终都是经过Gradle来实现的。因此学习Gradle很是重要。git

目前国内对Android领域的探索已经愈来愈深,很多技术领域如插件化、热修复、构建系统等都对Gradle有迫切的需求,不懂Gradle将没法完成上述事情。因此Gradle必需要学习。github

Gradle不仅仅是一个配置脚本,它的背后是几门语言:api

  • Groovy Language
  • Gradle DSL
  • Android DSL

DSL的全称是Domain Specific Language,即领域特定语言,或者直接翻译成“特定领域的语言”,再直接点,其实就是这个语言不通用,只能用于特定的某个领域,俗称“小语言”。所以DSL也是语言。微信

实际上,Gradle脚本大多都是使用groovy语言编写的。闭包

Groovy是一门jvm语言,功能比较强大,细节也不少,所有学习的话比较耗时,对咱们来讲收益较小,而且玩转Gradle并不须要学习Groovy的所有细节,因此其实咱们只须要学一些Groovy基础语法与API便可。app

为什么要使用 Groovy

Groovy是一种基于JVM的敏捷开发语言,它结合了众多脚本语言的强大的特性,因为同时又能与Java代码很好的结合。一句话:既有面向对象的特性又有纯粹的脚本语言的特性。jvm

因为Groovy运行在JVM上,所以也可使用Java语言编写的组建。ide

简单来讲,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