groovy1

参考

http://www.groovy-lang.org/documentation.html

总的帮助文档索引

http://www.groovy-lang.org/api.html

groovy本身的api

http://www.groovy-lang.org/gdk.html

groovy内部的java实现api

https://blog.csdn.net/mousebaby808/category_957087.html

前言

构建,叫 build 也好,叫 make 也行。反正就是根据输入信息然后干一堆事情,最后得到几个产出物(Artifact)。

最最简单的构建工具就是 make 了。 make 就是根据 Makefile 文件中写的规则,执行对应的命令,然后得到目标产物。

在 Gradle 爆红之前,常用的构建工具是 ANT,然后又进化到 Maven。 ANT 和 Maven这两个工具其实也还算方便, 现在还有很多地方在使用。但是二者都有一些缺点, 所以让更懒得人觉得不是那么方便。 比如, Maven 编译规则是用 XML 来编写的。 XML 虽然通俗易懂, 但是很难在 xml 中描述 if{某条件成立,编译某文件}/else{编译其他文件}这样有不同条件的任务。

怎么解决? 怎么解决好?对程序员而言, 自然是编程解决,但是有几个小要求:
l 这种“编程”不要搞得和程序员理解的编程那样复杂。寥寥几笔,轻轻松松把要做的事情描述出来就最好不过。 所以, Gradle 选择了 Groovy。 Groovy 基于 Java并拓展了 Java。 Java 程序员可以无缝切换到使用 Groovy 开发程序。

Groovy 说白了就是把写 Java 程序变得像写脚本一样简单。写完就可以执行, Groovy 内部会将其编译成 Java class 然后启动虚拟机来执行。当然,这些底层的渣活不需要你管。

l 除了可以用很灵活的语言来写构建规则外, Gradle 另外一个特点就是它是一种DSL,即 Domain Specific Language,领域相关语言。什么是 DSL,说白了它是某个行业中的行话。 

Gradle 中也有类似的行话,比如 sourceSets 代表源文件的集合等.....太多了,记不住。 以后我们都会接触到这些行话。那么,对使用者而言, 这些行话的好处是什么呢?这就是:一句行话可以包含很多意思,而且在这个行当里的人一听就懂,不用解释。另外,基于行话, 我们甚至可以建立一个模板, 使用者只要往这个模板里填必须要填的内容, Gradle就可以非常漂亮得完成工作,得到想要的东西。

Groovy 介绍

Groovy 是一种动态语言。这种语言比较有特点,它和 Java 一样,也运行于 Java 虚拟机中。恩??对头,简单粗暴点儿看,你可以认为 Groovy 扩展了 Java 语言。比如, Groovy对自己的定义就是: Groovy 是在 java 平台上的、 具有像 PythonRuby Smalltalk 语言特性的灵活动态语言, Groovy 保证了这些特性像 Java 语法一样被 Java 开发者使用。

除了语言和 Java 相通外, Groovy 有时候又像一种脚本语言。前文也提到过,当我执行Groovy 脚本时, Groovy 会先将其编译成 Java 类字节码,然后通过 Jvm 来执行这个 Java 类。

图 1 展示了 Java、 Groovy 和 Jvm 之间的关系。

wps1

实际上,由于 Groovy Code 在真正执行的时候已经变成了 Java 字节码,所以 JVM 根本不知道自己运行的是 Groovy 代码。
下面我们将介绍 Groovy。由于此文的主要目的是 Gradle,所以我们不会过多讨论 Groovy中细枝末节的东西,而是把知识点集中在以后和 Gradle 打交道时一些常用的地方上。

练习

既然是一种语言,那么边学边练习是最好的方式,那么怎么练习使用groovy代码呢,

在Idea/AS中,随便找一个项目,

wps2

然后就会自动在Scratches and consoles中创建一个.groovy文件,在此写groovy代码运行就行了,

注意Scratches and consoles创建的文件不是跟项目走的,会放到下边路径,

C:Userspuppet.AndroidStudio3.6configscratches

C:Userspuppet.AndroidStudio3.6configconsoles

也就是说在此创建了文件,那么在所有的项目中都能看到。

前提知识

为了后面讲述方面,这里先介绍一些前提知识。初期接触可能有些别扭,看习惯就好了。

语言特性

1. Groovy 注释标记和 Java 一样,支持//或者/**/

2. Groovy 语句可以不用分号结尾。 (Groovy 为了尽量减少代码的输入,确实煞费苦心)

3. Groovy 中支持动态类型,即定义变量的时候可以不指定其类型。 Groovy 中,变量定义可以使用关键字 def。 注意,虽然 def 不是必须的,但是为了代码清晰,建议还是使用 def 关键字

    def variable1 = 1 //可以不使用分号结尾
    def varable2 = "I am a person"
    def int x = 1 //变量定义时,也可以直接指定类型

4. 函数定义时,参数的类型也可以不指定。比如

    String testFunction(arg1,arg2){//无需指定参数类型
        ...
    }

5. 除了变量定义可以不指定类型外, Groovy 中函数的返回值也可以是无类型的。

比如:

    //无类型的函数定义,必须使用 def 关键字
    def nonReturnTypeFunc(){
        last_line //最后一行代码的执行结果就是本函数的返回值
    }

    //如果指定了函数返回类型,则可不必加 def 关键字来定义函数
    String getString(){
        return "I am a string"
    }

其实,所谓的无返回类型的函数,我估计内部都是按返回 Object 类型来处理的。毕竟, Groovy 是基于 Java 的,而且最终会转成 Java Code 运行在 JVM 上

6. 函数返回值: Groovy 的函数里,可以不使用 return xxx 来设置 xxx 为函数返回值。如果不使用 return 语句的话,则函数里最后一句代码的执行结果被设置成返回值。比如

    //下面这个函数的返回值是字符串"getSomething return value"
    def getSomething(){
        "getSomething return value" //如果这是最后一行代码,则返回类型为 String
        1000 //如果这是最后一行代码,则返回类型为 Integer
    }

注意,如果函数定义时候指明了返回值类型的话,函数中则必须返回正确的数据类型,否则运行时报错。如果使用了动态类型的话,你就可以返回任何类型了。

7. Groovy 对字符串支持相当强大,充分吸收了一些脚本语言的优点:

l 单引号'...'中的内容严格对应 Java 中的 String,不对$符号进行转义

def singleQuote='I am $ dolloar' //输出就是 I am $ dolloar

l 双引号"..."的内容则和脚本语言的处理有点像,如果字符中有$号的话,则它会d对$表达式先求值。

def doubleQuoteWithoutDollar = "I am one dollar" //输出 I am one dollar

def x = 1

def doubleQuoteWithDollar = "I am $x dolloar" //输出 I am 1 dolloar

l 三个引号'''xxx'''中的字符串支持随意换行 比如

def multieLines = ''' begin

line 1

line 2

end '''

8. Groovy 中函数调用的时候还可以不加括号。比如:

println("test") ---> println "test"

注意,虽然写代码的时候,对于函数调用可以不带括号,但是 Groovy 经常把属性和函数调用混淆。

比如

def getSomething(){
    "hello"
}
getSomething() //如果不加括号的话, Groovy 会误认为 getSomething 是一个变量。 

wps3

所以,调用函数要不要带括号,我个人意见是如果这个函数是 Groovy API 或者 Gradle API 中比较常用的,比如 println,就可以不带括号。否则还是带括号。 Groovy 自己也没有太好的办法解决这个问题,只能兵来将挡水来土掩了。

9.  groovy中的==

l 在java中==表示引用相同;

l 而在groovy中如果对象实现了Comparable,则会通过a.compareTo(b)==0来表示是否相等;

l 如果如果对象没有实现Comparable,则会用a.equals(b)来表示是否相等;

l 在groovy中如果想要判断两个对象引用是否相等,使用a.is(b)

10. groovy中的方法如果不声明访问权限,默认为public

11. assert

如果assert判断的代码不为true,则直接停止运行并报出错误,后边代码不会执行。

如果true,不会有任何输出,直接执行之后的代码。

默认导入

All these packages and classes are imported by default, i.e. you do not have to use an explicit import statement to use them:

java.io.*

java.lang.*

java.math.BigDecimal

java.math.BigInteger

java.net.*

java.util.*

groovy.lang.*

groovy.util.*

重载方法

In groovy the method will be chosen based on the types of the arguments at runtime.

In Java, this is the opposite: methods are chosen at compile time, based on the declared types.

The following code, written as Java code, can be compiled in both Java and Groovy, but it will behave differently:

int method(String arg) {
    return 1;
}
int method(Object arg) {
    return 2;
}
Object o = "Object";
int result = method(o);

In Java, you would have:

assertEquals(2, result);

Whereas in Groovy:

assertEquals(1, result);

That is because Java will use the static information type, which is that o is declared as an Object.

whereas(然而) Groovy will choose at runtime, when the method is actually called. Since(因为) it is called with a String, then the String version is called.

groovy属性方法直接使用

前面讲这些东西,主要是让大家了解 Groovy 的语法。实际上在 coding 的时候,是离不开 SDK 的。由于 Groovy 是动态语言,所以要使用它的 SDK 也需要掌握一些小诀窍。

以 Range 为例,我们该如何更好得使用它呢?

l 先定位到 Range 类。它位于 groovy.lang 包中:有了 API 文档,你就可以放心调用其中的函数了。 不过,不过,不过: 我们刚才代码

中用到了 Range.from/to 属性值,但翻看 Range API 文档的时候,其实并没有这两个成员变量。 图 6 是 Range 的方法

wps4

文档中并没有说明 Range 有 from 和 to 这两个属性,但是却有 getFrom 和 getTo 这两个函数。

What happened?

原来:根据 Groovy 的原则,如果一个类中有名为 xxyyzz 这样的属性(其实就是成员变量),Groovy 会自动为它添加 getXxyyzz 和 setXxyyzz 两个函数,用于获取和设置 xxyyzz 属性值。

所以,当你看到 Range 中有 getFrom 和 getTo 这两个函数时候,就得知道潜规则下,Range有 from 和 to 这两个属性。当然,由于它们不可以被外界设置,所以没有公开 setFrom 和 setTo函数

注意: get 和 set 后第一个字母是大写的

groovy创建对象传参

class Server {

    String name

    Cluster cluster

}

可以这样对Server的变量赋值:

def server = new Server()

server.name = "Obelix"

server.cluster = aCluster

也可以直接用构造函数赋值:

def server = new Server(name: "Obelix", cluster: aCluster)

可以使用with()简化赋值操作

在对对象成员赋值时,例如想要如下操作:

server.name = application.name

server.status = status

server.sessionCount = 3

server.start()

server.stop()

使用groovy的with

server.with {

    name = application.name

    status = status

    sessionCount = 3

    start()

    stop()

}

循环

对于整数,Groovy增加了如下几个方法来进行循环:

l upto:n.upto(m) 函数,表示循环 m- n 次,并且会有一个循环变量it,从n开始,每次循环增加1,直到m。循环体写在upto方法之后大括号中,表示一个闭包,在闭包中,it作为循环变量,值从a增长到n;

l times:n.times 函数,表示循环n次,循环变量it从0开始到n结束。

l step:n.step(x, y) 函数,表示循环变量从n开始到x结束,每次循环后循环变量增加y,所以整个循环次数为 (x - n) / y 次;

导入别名

在java中,当使用两个不同包里的相同类名的类时,只能导入一个,而另一个需要写全称,

如java.util.List和java.awt.List。

为了改善,groovy引入的导入别名:

import java.util.List as UtilList
import java.awt.List as AwtList
import javax.swing.WindowConstants as WC

UtilList list1 = [WC.EXIT_ON_CLOSE]
assert list1.size() instanceof Integer
def list2 = new AwtList()
assert list2.size() instanceof java.awt.Dimension

你也可以对静态导入进行别名:
import static java.lang.Math.abs as mabs
assert mabs(-4) == 4

你也可以对静态导入进行别名:

import static java.lang.Math.abs as mabs

assert mabs(-4) == 4

groovy中独有的关键词

groovy中有比java多几个关键词:

as

1.上边说的导入别名

2.类型转换

java:

List list = new ArrayList();
list.add("1");
list.add("2");
ArrayList list1 = (ArrayList)list;

groovy:

List list = ['a','b']
ArrayList list1 = list as ArrayList
println list1

Groovy语言的强制类型转换并不一定非要“as”关键字不可,比如上面的代码完全可以写成下面的样子:

ArrayList list1 = list

def

声明变量时使用,

在声明变量时也可以不用def,直接写 变量=xxx

in

在对集合、数组遍历时可以使用:

// iterate over a range

def x = 0
for ( i in 0..9 ) {
    x += i
}
assert x == 45

// iterate over a list

x = 0
for ( i in [0, 1, 2, 3, 4] ) {
    x += i
}
assert x == 10

// iterate over an array

def array = (0..4).toArray()
x = 0
for ( i in array ) {
    x += i
}
assert x == 10

// iterate over a map

def map = ['abc':1, 'def':2, 'xyz':3]
x = 0
for ( e in map ) {
    x += e.value
}
assert x == 6

// iterate over values in a map

x = 0
for ( v in map.values() ) {
    x += v
}
assert x == 6

// iterate over the characters in a string

def text = "abc"
def list = []
for (c in text) {
    list.add(c)
}
assert list == ["a", "b", "c"]

trait

类似于多重继承,但实现是用implement

groovy操作符

groovy操作符.docx

好了,了解上面一些基础知识后,我们再介绍点深入的内容。

Groovy 中的数据类型

Groovy 中的数据类型我们就介绍两种和 Java 不太一样的:

l 一个是 Java 中的基本数据类型。

l 另外一个是 Groovy 中的容器类。

l 最后一个非常重要的是闭包。

基本数据类型

作为动态语言, Groovy 世界中的所有事物都是对象。 所以, int, boolean 这些 Java 中的基本数据类型,在 Groovy 代码中其实对应的是它们的包装数据类型。 比如 int 对应为Integer, boolean 对应为 Boolean。

数组

因为在groovy中{}被用来表示closure,所以数组用[]表示

int[] array = [1,2,3]

容器类

Groovy 中的容器类很简单,就三种:

l List:链表,其底层对应 Java 中的 List 接口,一般用 ArrayList 作为真正的实现类。

l Map:键-值表,其底层对应 Java 中的 LinkedHashMap。

l Range:范围,它其实是 List 的一种拓展。

对容器而言,我们最重要的是了解它们的用法。 下面是一些简单的例子:

1. List 类

变量定义: List 变量由[]定义,比如

def aList = [5,'string',true] //List 由[]定义,其元素可以是任何对象

变量存取:可以直接通过索引存取,而且不用担心索引越界。如果索引超过当前链表长度, List 会自动往该索引添加元素

assert aList[1] == 'string'

assert aList[5] == null //第 6 个元素为空

aList[100] = 100 //设置第 101 个元素的值为 10

assert aList[100] == 100

那么, aList 到现在为止有多少个元素呢?

println aList.size ===>结果是 101

2. Range 类

Range 是 Groovy 对 List 的一种拓展,

Range 类型的变量 由 begin 值+两个点+end 值表示;

大体的使用方法如下:

def aRange = 1..5

这个 aRange 包含 1,2,3,4,5 这 5 个值

如果不想包含最后一个元素,则

def aRangeWithoutEnd = 1..<5

包含 1,2,3,4 这 4 个元素

用在循环中:

for (i in 1..10)

from和to

println aRange.from

println aRange.to

3. Map 类

Map 由[:]定义, 注意其中的冒号。冒号左边是 key,右边是 Value。

key 必须是字符串, value 可以是任何对象。另外, key 可以用''或""包起来,也可以不用引号包起来。

比如:

def aNewMap = [key1:"value",key2:true] //其中的 key1 和 key2 默认被处理成字符串"key1"和"key2"

不过 Key 要是不使用引号包起来的话,也会带来一定混淆,比如

def key1="wowo"
def aConfusedMap=[key1:"who am i?"]

aConfuseMap 中的 key1 到底是"key1"还是变量 key1 的值“wowo”?

显然,答案是字符串"key1"。

如果要是"wowo"的话,则 aConfusedMap 的定义必须设置成:

def aConfusedMap=[“$key1”:"who am i?"]

或 def aConfusedMap=[(key1):"who am i?"]

Map 中元素的存取更加方便,它支持多种方法:

println aMap.keyName <==这种表达方法好像 key 就是 aMap 的一个成员变量一样
println aMap['keyName'] <==这种表达方法更传统一点
aMap.faq = "you" <==为 map 添加新元素

对于方法的参数时map时,

def testMap(Map<String, ?> map) {
    map.each {
        println(it.key + ":" + it.value)
    }
}
testMap haha: 1
testMap([haha: 1, haha2: 2])

调用方法时,

l 可以传递map对象,

l 如果直接传递,只有一个键值对,那么不用[]和()就行,

l 如果直接传递,有两个以上的键值对,那么需要[]和()。

闭包(Closure)

closures和lambda表达式是不一样的,不能搞混淆

简介

闭包,英文叫 Closure, 是 Groovy 中非常重要的一个数据类型或者说一种概念了。

闭包,代表了一段可执行的代码。其外形如下:

    def aClosure = {//闭包是一段代码,所以需要用花括号括起来..
        String param1, int param2 -> //这个箭头很关键。箭头前面是参数定义,箭头后面是代码
        println "this is code" //这是代码, 最后一句是返回值,也可以使用 return,和 Groovy 中普通函数一样
    }

简而言之, Closure 的定义格式是:

    def xxx = {paramters -> code} //或者
    def xxx = {无参数,纯 code} 这种 case 不需要->符号
    说实话,从 C/C++语言的角度看,闭包和函数指针很像。

闭包定义好后,要调用它的方法就是:

    闭包对象.call(参数) 或者更像函数指针调用的方法:
    闭包对象(参数)

比如:

aClosure.call("this is string", 100)
aClosure("this is string", 100)

上面就是一个闭包的定义和使用,在闭包中, 还需要注意一点:

如果闭包没定义参数的话,则隐含有一个参数,这个参数名字叫 it。

比如:

    def greeting = { "Hello, $it!" }
    assert greeting('Patrick') == 'Hello, Patrick!'

    等同于:
    def greeting = { it -> "Hello, $it!" }
    assert greeting('Patrick') == 'Hello, Patrick!'

但是,如果在闭包定义时, 采用下面这种写法,则表示闭包没有参数!

    def noParamClosure = { -> true }
    这个时候,我们就不能给 noParamClosure 传参数了!
    noParamClosure ("test") <==报错喔!

Delegation

Owner, delegate and this

在一个Closure中,有

getThisObject()  this

getDelegate()   delegate

getOwner()     owner

为了理解delegate的概念,需要了解一下这几个东西。

一个closure实现上定义了三个不同的对象:

l this

l owner

l delegate

this

表示 创建此closure的类的对象,

closure内部closure的this也引用的是外部closure的this

class Enclosing {

    void run() {

        def whatIsThisObject = { getThisObject() }         

        assert whatIsThisObject() == this                  

        def whatIsThis = { this }                          

        assert whatIsThis() == this                        

    }

}

class EnclosedInInnerClass {

    class Inner {

        Closure cl = { this }                              

    }

    void run() {

        def inner = new Inner()

        assert inner.cl() == inner                         

    }

}

class NestedClosures {

    void run() {

        def nestedClosures = {

            def cl = { this }                              

            cl()

        }

        assert nestedClosures() == this                    

    }

}

owner

表示创建此closure的类/closure的对象,

closure内部closure的owner引用的是外部closure对象

class Enclosing {

    void run() {

        def whatIsOwnerMethod = { getOwner() }              

        assert whatIsOwnerMethod() == this  

        def whatIsOwner = { owner }                         

        assert whatIsOwner() == this                        

    }

}

class EnclosedInInnerClass {

    class Inner {

        Closure cl = { owner }                              

    }

    void run() {

        def inner = new Inner()

        assert inner.cl() == inner                          

    }

}

class NestedClosures {

    void run() {

        def nestedClosures = {

            def cl = { owner }                              

            cl()

        }

        assert nestedClosures() == nestedClosures           

    }

}

delegate

delegate corresponds(相当于) to a third party object where methods calls or properties are resolved whenever the receiver of the message is not defined

By default, the delegate is set to owner:

class Enclosing {

    void run() {

        def cl = { getDelegate() }                         

        def cl2 = { delegate }                             

        assert cl() == cl2()                               

        assert cl() == this                                

        def enclosed = {

            { -> delegate }.call()                         

        }

        assert enclosed() == enclosed                      

    }

}

Delegation strategy

上边使用代理好像没什么区别,delegate的作用就是为了在closure里调用的代码,如果涉及到外部变量/方法时,根据不同的strategy来在不同的对象中找对应的变量/方法。

默认的strategy是Closure.OWNER_FIRST。

下边是可以使用的strategy:

l Closure.OWNER_FIRST

is the default strategy. If a property/method exists on the owner, then it will be called on the owner. If not, then the delegate is used.

找方法/变量先在owner中找,如果找不到再从delegate中找。

l Closure.DELEGATE_FIRST

reverses the logic: the delegate is used first, then the owner

和第一个相反。

l Closure.OWNER_ONLY

will only resolve the property/method lookup on the owner: the delegate will be ignored.

只在owner中找。

l Closure.DELEGATE_ONLY

will only resolve the property/method lookup on the delegate: the owner will be ignored.

只在delegate中找

l Closure.TO_SELF

can be used by developers who need advanced meta-programming techniques and wish to implement a custom resolution strategy: the resolution will not be made on the owner or the delegate but only on the closure class itself. It makes only sense to use this if you implement your own subclass of Closure.

其实就是自定义strategy。

通过例子解释:

    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
    }

Closure 使用中的注意点

1. 省略圆括号

闭包在 Groovy 中大量使用,比如很多类都定义了一些函数,这些函数最后一个参数都是一个闭包。比如:

public static <T> List<T> each(List<T> self, Closure closure)

上面这个函数表示针对 List的每一个元素都会调用 closure做一些处理。

这里的 closure,就有点回调函数的感觉。但是,在使用这个 each 函数的时候,我们传递一个怎样的 Closure进去呢? 比如:

    def iamList = [1,2,3,4,5] //定义一个 List
    iamList.each{ //调用它的 each,这段代码的格式看不懂了吧? each 是个函数,圆括号去哪了?
        println it
    }

上面代码有两个知识点:

each 函数调用的圆括号不见了!原来, Groovy 中,当函数的最后一个参数是闭包的话,可以省略圆括号。比如

    def testClosure(int a1,String b1, Closure closure){
        //do something
        closure() //调用闭包
    }
    那么调用的时候,就可以免括号!
    testClosure (4, "test", {
        println "i am in closure"
    } ) //红色的括号可以不写..

注意,这个特点非常关键,因为以后在 Gradle 中经常会出现图 7 这样的代码:

wps5

经常碰见图 7 这样的没有圆括号的代码。省略圆括号虽然使得代码简洁,看起来更像脚本语言,但是它这经常会让我 confuse(不知道其他人是否有同感),以 doLast 为例,完整的代码应该按下面这种写法:

    doLast({
        println 'Hello world!'
    })

有了圆括号,你会知道 doLast 只是把一个 Closure 对象传了进去。 很明显,它不代表这段脚本解析到 doLast 的时候就会调用 println 'Hello world!' 。

但是把圆括号去掉后,就感觉好像 println 'Hello world!'立即就会被调用一样!

2. 如何确定 Closure 的参数

另外一个比较让人头疼的地方是, Closure 的参数该怎么搞? 还是刚才的 each 函数:

public static <T> List<T> each(List<T> self, Closure closure)

如何使用它呢?比如:

    def iamList = [1,2,3,4,5] //定义一个 List 变量
    iamList.each{ //调用它的 each 函数,只要传入一个 Closure 就可以了。
        println it
    }

看起来很轻松,其实:对于 each 所需要的 Closure,它的参数是什么?有多少个参数?返回值是什么?

我们能写成下面这样吗?

    iamList.each{String name,int x ->
        return x
    } //运行的时候肯定报错!

所以, Closure 虽然很方便,但是它一定会和使用它的上下文有极强的关联。要不,作为类似回调这样的东西,我如何知道调用者传递什么参数给 Closure 呢?

此问题如何破解?只能通过查询 API 文档才能了解上下文语义。

脚本类

Groovy 中可以像 Java 那样写 package,然后写类,如果不声明 public/private等访问权限的话, Groovy 中类及其变量默认都是 public 的 。

脚本到底是什么

Java中,我们最熟悉的是类。但是我们在 Java的一个源码文件中,不能不写 class (interface或者其他....),

而 Groovy 可以像写脚本一样,把要做的事情都写在 xxx.groovy 中,而且可以通过 groovy xxx.groovy 直接执行这个脚本。

这到底是怎么搞的?

既然是基于 Java 的, Groovy 会先把 xxx.groovy 中的内容转换成一个 Java 类。

比如:test.groovy 的代码是:

println 'Groovy world!'

Groovy 把它转换成这样的 Java 类:

执行 groovyc -d classes test.groovy

groovyc 是 groovy 的编译命令, -d classes 用于将编译得到的 class 文件拷贝到 classes 文件夹下

图是 test.groovy 脚本转换得到的 java class。用 jd-gui 反编译它的代码:

wps6

l test.groovy 被转换成了一个 test 类,它从 script 派生。

l 每一个脚本都会生成一个 static main 函数。这样,当我们 groovy test.groovy 的时候,其实就是用 java 去执行这个 main 函数

l 脚本中的所有代码都会放到 run 函数中。比如, println 'Groovy world',这句代码实际上是包含在 run 函数里的。

l 如果脚本中定义了函数,则函数会被定义在 test 类中。

脚本中的变量和作用域

前面说了, xxx.groovy 只要不是和 Java 那样的 class,那么它就是一个脚本。而且脚本的代码其实都会被放到 run 函数中去执行。

那么,在 Groovy 的脚本中,很重要的一点就是脚本中定义的变量和它的作用域。

举例:

    def x = 1 <==注意,这个 x 有 def(或者指明类型,比如 int x = 1)
    def printx(){
        println x
    }
    printx() <==报错,说 x 找不到 

wps7

printx 被定义成 test 类的成员函数
def x = 1,这句话是在 run 中创建的。所以, x=1 从代码上看好像是在整个脚本中定义的,但实际上 printx 访问不了它。

printx 是 test 成员函数,除非 x 也被定义成 test 的成员函数,否则 printx 不能访问它。
那么,如何使得 printx 能访问 x 呢?很简单,定义的时候不要加类型和 def。

这次 Java 源码又变成什么样了呢?

wps8

x 也没有被定义成 test 的成员函数,而是在 run 的执行过程中,将 x 作为一个属性添加到 test 实例对象中了。然后在 printx 中,先获取这个属性。

虽然 printx 可以访问 x 变量了,但是假如有其他脚本却无法访问 x 变量。因为它不是 test的成员变量。

怎么办?

import groovy.transform.Field; //必须要先 import
@Field x = 1 <==在 x 前面加上@Field 标注,这样, x 就彻彻底底是 test 的成员变量了。

查看编译后的 test.class 文件,得到:

wps9

这个时候, test.groovy 中的 x 就成了 test 类的成员函数了。如此,我们可以在 script 中定义那些需要输出给外部脚本或类使用的变量了!

文件 I/O 操作

本节介绍下 Groovy 的文件 I/O 操作。直接来看例子吧,虽然比 Java 看起来简单,但要理解起来其实比较难。尤其是当你要自己查 SDK 并编写代码的时候。
整体说来, Groovy 的 I/O 操作是在原有 Java I/O 操作上进行了更为简单方便的封装,并且使用 Closure 来简化代码编写。主要封装了如下一些了类:

java.io.File: http://docs.groovy-lang.org/latest/html/groovy-jdk/java/io/File.html
java.io.InputStream: http://docs.groovy-lang.org/latest/html/groovy-jdk/java/io/InputStream.html
java.io.OutputStream: http://docs.groovy-lang.org/latest/html/groovy-jdk/java/io/OutputStream.html
java.io.Reader: http://docs.groovy-lang.org/latest/html/groovy-jdk/java/io/Reader.html
java.io.Writer: http://docs.groovy-lang.org/latest/html/groovy-jdk/java/io/Writer.html
java.nio.file.Path: http://docs.groovy-lang.org/latest/html/groovy-jdk/java/nio/file/Path.html 

读文件

Groovy 中,文件读操作简单到令人发指:

def targetFile = new File(文件名) <==File 对象还是要创建的。

然后打开 http://docs.groovy-lang.org/latest/html/groovy-jdk/java/io/File.html

看看 Groovy 定义的 API:

1 读该文件中的每一行: eachLine 的唯一参数是一个 Closure。 Closure 的参数是文件每一行的内容其内部实现肯定是 Groovy 打开这个文件,然后读取文件的一行,然后调用 Closure...

    targetFile.eachLine{
        String oneLine ->
        println oneLine
    } <==是不是令人发指?? !

2 直接得到文件内容

targetFile.getBytes() <==文件内容一次性读出,返回类型为 byte[]

注意前面提到的 getter 和 setter 函数,这里可以直接使用 targetFile.bytes //....

3 使用 InputStream 的 SDK 在http://docs.groovy-lang.org/latest/html/groovy-jdk/java/io/InputStream.html

    def ism = targetFile.newInputStream()
    //操作 ism,最后记得关掉
    ism.close

4 使用闭包操作 inputStream,以后在 Gradle 里会常看到这种搞法

    targetFile.withInputStream{ ism ->
        操作 ism. 不用 close。 Groovy 会自动替你 close
    }

写文件

和读文件差不多。不再啰嗦。这里给个例子,告诉大家如何 copy 文件。

    def srcFile = new File(源文件名)
    def targetFile = new File(目标文件名)
    targetFile.withOutputStream{ os->
        srcFile.withInputStream{ ins->
            os << ins //利用 OutputStream 的<<操作符重载,完成从 inputstream 到 OutputStream的输出
        }
    }

os << ins

leftShift操作,Pipe an InputStream into an OutputStream for efficient stream copying.

xml文件操作

除了 I/O 异常简单之外, Groovy 中的 XML 操作也极致得很。 Groovy 中, XML 的解析提供了和 XPath 类似的方法,名为 GPath。这是一个类,提供相应 API。关于 XPath,请脑补 https://en.wikipedia.org/wiki/XPath。

GPath 功能包括: 给个例子好了,来自 Groovy 官方文档。

test.xml 文件:

    <response version-api="2.0">
        <value>
            <books>
                <book available="20" id="1">
                    <title>Don Xijote</title>
                    <author id="1">Manuel De Cervantes</author>
                </book>
                
                <book available="14" id="2">
                    <title>Catcher in the Rye</title>
                    <author id="2">JD Salinger</author>
                </book>
                
                <book available="13" id="3">
                    <title>Alice in Wonderland</title>
                    <author id="3">Lewis Carroll</author>
                </book>
                
                <book available="5" id="4">
                    <title>Don Xijote</title>
                    <author id="4">Manuel De Cervantes</author>
                </book>
            </books>
        </value>
    </response>

现在来看怎么玩转 GPath:

    //第一步,创建 XmlSlurper 类
    def xparser = new XmlSlurper()
    def targetFile = new File("test.xml")

    //轰轰的 GPath 出场
    GPathResult gpathResult = xparser.parse(targetFile)

    //开始玩 test.xml。现在我要访问 id=4 的 book 元素。
    //下面这种搞法, gpathResult 代表根元素 response。通过 e1.e2.e3 这种//格式就能访问到各级子元素....
    def book4 = gpathResult.value.books.book[3]

//得到 book4 的 author 元素

    def author = book4.author

//再来获取元素的属性和 textvalue

    assert author.text() == ' Manuel De Cervantes '

获取属性更直观

author.@id == '4' 
或者 author['@id'] == '4'

属性一般是字符串,可通过 toInteger 转换成整数

author.@id.toInteger() == 4

好了。 GPath 就说到这。再看个例子。我在使用 Gradle 的时候有个需求,就是获取AndroidManifest.xml 版本号(versionName)。有了 GPath,一行代码搞定,请看:

    def androidManifest = new XmlSlurper().parse("AndroidManifest.xml")
    println androidManifest['@android:versionName']
    或者
    println androidManifest.@'android:versionName’
原文地址:https://www.cnblogs.com/muouren/p/12773319.html