【Gradle】Groovy筑基(一)变量与闭包

1,470 阅读4分钟

一、变量与字符串

1. 1 变量

Groovy变量没有基本类型变量,所有的变量都是对象类型,在定义变量的时候,无论定义类型是基本类型还是对象类型,最后都会被统一转换成对象类型

下面为测试代码:

int x = 10
Integer y = 20
double k = 3.14
Double z = 3.1415926

println x.class
println y.class
println k.class
println z.class

输出结果如下:

image.png

而在Groovy有两种方式可以定义变量:

  • 强类型定义 在定义一个变量的时候,同时指定这个变量的类型,如果要更改这个变量的值的时候,只能更改为同类型的 数值。以下为强类型定义变量:
int x = 1
double y = 2.0
char a = 'a'
  • 弱类型定义 在定义一个变量的时候,不需要提前指定变量的类型,而是由编译器去推断该类型,这样的好处是如果你要更改这个变量的类型的话,是不会报错的。以下为弱类型定义:
// 使用def关键字让编译器推断类型
def a = 3
println a.class
a = false
println a.class

输出结果如下:

image.png

可以看到一开始的变量a是Integer类型,当更改a的值后,变量a变成了Boolean类型,弱类型定义变量对于类型的转换很方便。

一般而言,如果变量只在本模块内部使用,则推荐使用弱类型定义;而如果是暴露给外界使用的变量,则推荐使用强类型定义,可以避免外界随意转换类型。

1. 2字符串

Groovy中的字符串对应Java的String类,我们可以直接使用String,也可以使用Groovy提供的GString字符串类。在Groovy中定义字符串有三种不同方式(这里不包括new String()方式),分别如下:

def a = 'hello world'      // 单引号定义
def b = "hello world"      // 双引号定义(Java只能双引号定义)
def c = '''hello world'''  // 三引号定义

当把这些变量的class打印出来的话,可以看到它们所属的对象类型都是class java.lang.String。当然,如果仅仅只是这样定义字符串的话,这三种方式都是没有区别的,可以使用任意一种,接下来来讲讲这几种方式的区别:

  • 单引号或双引号与三引号的区别在于单引号跟双引号定义的字符串是没有格式的,而三引号定义的字符串是有格式的,也就是说三引号能把字符串所有的内容原原本本地展示出来,而不需要额外的转义符。

下面来看一个例子:

def a = 'hello \n \' \nworld'
def b = '''hello
 '
world'''

println a
println b

println a.class
println b.class

先来看看上面代码的输出结果:

image.png

通过结果我们可以看出,如果使用单引号定义字符串,当字符串想要输出换行或者一些需要转义的字符时,需要用到"\n"或者""来帮助输出(双引号同理) ,而通过三引号定义的字符串则可以原原本本地把格式输出来。

上面输出的class这里暂时先不用管,先看下面。

  • 双引号与单引号或三引号的区别在于双引号可以直接通过${}来拼接变量,Groovy在这点上是与kotlin一样的;而单引号跟三引号需要拼接变量的话,需要通过+号拼接,与Java一样
def c = "123"
def d = "hello ${c} world"

println d
println d.class

输出结果:

image.png

这里需要结合上面输出的class来对比了,单引号跟三引号的对象类型是Java的String,而双引号使用${}拼接出的字符串对象类型是Groovy的GString(不使用拼接的话双引号的对象类型也是Java的String)。

1.2.1 字符串的常用API

  • 比较字符串的大小
def str1 = 'hello'
def str2 = "Hello"

println str1 > str2 // 打印的结果为true,通过ASCII码表进行比较
  • 截取部分字符串
def str1 = 'hello'
println str1[1..3] // 打印的结果是ell
  • 对字符串做减法
def str1 = 'hello'
def str2 = "el"

println str1 - str2 // 打印的结果是hlo
  • 字符串逆序
def str1 = 'hello'
println str1.reverse() // 打印的结果是olleh
  • 首字母大写
def str1 = 'hello'
println str1.capitalize() // 打印的结果是Hello
  • 字符串是否全都为数字
def str1 = '123'
def str2 = "Hello"

println str1.isNumber() // 打印的结果是true
println str2.isNumber() // 打印的结果是false

二、闭包的定义与使用

2. 1 闭包的定义与简单使用

Groovy这门语言的一大特性就是它的闭包比其他所有类型的语言闭包都要强大,定义一个闭包很简单,下面来看一下:

  • 无参数/默认参数写法
def closure = {
    println("hello groovy")
}

closure.call()    // 无参写法
closure.call(123) // 默认参数写法

在定义一个闭包的时候,如果不定义参数的话,是会默认有一个参数的,可以直接在调用该闭包的时候直接传值进去即可。

  • 有参数写法
def closure = { String name, int age ->
    println("hello $name, age is $age")
}

closure.call("柚子啊", 18)

2. 2 字符串String的常用闭包

  • 字符串遍历
def str = "2 and 3 is 5"
str.each {
    // 每个字符都输出两遍
    print it * 2
}

// 打印的结果为"22  aanndd  33  iiss  55"
  • 查找符合条件的第一个字符
def str = "2 and 3 is 5"
println str.find {
    it.isNumber()
}

// 打印的结果是2
  • 查找符合条件的所有字符
def str = "2 and 3 is 5"
println str.findAll {
    it.isNumber()
}

// 打印的结果是[2, 3, 5]
  • 查找是否存在符合条件的字符
def str1 = "2 and 3 is 5"
def str2 = 'hello'
println str1.any {
    it.isNumber()
}
// 打印的结果为true

println str2.any {
    it.isNumber()
}
// 打印的结果为false
  • 对字符串中的每一位字符进行操作
def str = "Hello World"
println str.collect {
    it.toUpperCase()
}

// 打印的结果为[H, E, L, L, O,  , W, O, R, L, D]

2. 3 闭包的委托策略

2.3.1 闭包的关键变量(可代表当前对象)

闭包的三个关键变量为this、owner、delegate,这三个变量,在同一个闭包中,都表示同一个对象;

  • this:代表闭包定义的类
  • owner:代表闭包定义的类或对象
  • delegate:代表任意对象,默认为owner指向的对象

1)在同一个闭包中,都表示同一个对象:

def scriptClosure = {
    println this
    println owner
    println delegate
}
scriptClosure.call()

打印结果为:

image.png

2)在一个闭包里定义另一个闭包,此时再来看看区别:

// 外部闭包
def nestClosure = {
    // 内部闭包
    def innerClosure = {
        println this
        println owner
        println delegate
    }
    innerClosure.call()
}
nestClosure.call()

打印结果为:

image.png

从打印结果可以看出,this代表的是内部闭包的对象,而owner跟delegate代表的是外部闭包的对象。

3)delegate指向其他对象 上面的例子可以看出owner跟delegate代表的对象是一样的,现在来看一下这两个变量在其他场景的区别:

// 随便定义的类,只为了测试
class Person {

}

Person person = new Person()
def nestClosure = {
    def innerClosure = {
        println this
        println owner
        println delegate
    }
    // 将闭包的delegate执行person对象
    innerClosure.delegate = person
    innerClosure.call()
}
nestClosure.call()

打印结果如下:

image.png

正如一开始解释this、owner、delegate的区别的时候所说的,delegate可以表示任意对象,而owner在这个例子中只能表示外部闭包。

2.3.2 闭包的委托策略

先来看几行代码:

class Student {
    String name
    def pretty = {
        "My name is $name"
    }

    String toString() {
        pretty.call()
    }
}
def student = new Student(name: "Miya")
println student.toString()

此时打印出来的结果正是预期的结果:

My name is Miya

而这时我们在上面的代码上再新增几行代码,如下所示:

class Student {
    String name
    def pretty = {
        "My name is $name"
    }

    String toString() {
        pretty.call()
    }
}

// 新增--Teacher类
class Teacher {
    String name
}

def student = new Student(name: "Miya")
// 新增--teacher对象
def teacher = new Teacher(name: "Youzi")
// 新增--设置student对象中的delegate指向teacher对象
student.pretty.delegate = teacher
// 新增--修改student中的委托策略优先级为DELEGATE_FIRST
student.pretty.resolveStrategy = Closure.DELEGATE_FIRST

println student.toString()

此时再打印结果看看:

My name is Youzi

成功的把teacher对象中的name打印了出来,注释已经写的很清楚了,这就是闭包的委托策略。