上一章已经讲解完字符串、集合、运算符重载等,接下来讲解闭包、流程控制、方法、类等核心基础。
闭包
闭包是一种表示可以执行代码块的方法,且闭包也是对象,可以像方法一样传递参数。在声明闭包后,可以使用并修改其作用域内的所有变量值。
闭包语法定义
参数列表 -> 实现体
不带参数的闭包,可以省略参数和->,调用直接调用call方法
// 闭包及其调用方法
def closure = { println "Hello Groovy" }
closure.call() // Hello Groovy
闭包可以像方法一样,在闭包中声明中引入形参;
// 参数化的闭包
def closure = { name -> println "Hello ${name}" }
closure.call("Groovy") // Hello Groovy
// 省略了call
closure("Gradle") // Hello Gradle
在闭包中,如果只有一个形参时,可以省略形参名,直接只用隐参数it;当有多个参数时,it就不能表示了。
// 单个隐参数
def closure = { println "Hello ${it}" }
closure.call("Groovy") // Hello Groovy
closure("Gradle") // Hello Gradle
// 多个参数
eachMap { k, v ->
println "${k}, ${v}"
}
/**
* 自定义遍历Map的函数,参数接受一个闭包
*/
def eachMap(closure) {
def map = ['name': 'kerwin', 'age': 12]
map.each {
closure.call(it.key, it.value)
}
}
闭包可以访问属性值
// 闭包和作用范围
def string = "Hello"
def closure = { name -> println "${string} ${name}" }
closure.call("Groovy") // Hello Groovy
// 修改属性值
string = "Welcome"
closure.call("Groovy") // Welcome Groovy
闭包的作用域
def string = "Hello"
def closure = { name -> println "${string} ${name}" }
closure.call("Groovy") // Hello Groovy
string = "Welcome"
closure.call("Groovy") // Welcome Groovy
def test(clos) {
def string = "My" // 不生效的代码
clos.call("book")
}
test(closure) // Welcome book
/**
* 闭包在实参列表外部
*/
// 调用闭包对象的引用
test closure // Welcome book
// 调用闭字面值
test {
name -> println "Welcome ${name}"
} // Welcome book
upto方法在每次调用闭包时,其迭代都从它接受的数字值开始,止于给定的参数值。downto方法在每次调用闭包时,其迭代都从他给定的参数值开始,止于他接受的数字值。下面使用闭包求阶乘示例
println fac(10) // 55
/**
* 求number的阶乘, upto
*/
def fac(int number) {
def result = 0
1.upto(number) {
num -> result += num
}
result
}
println fac2(10) // 55
/**
* 求number的阶乘, downto
*/
def fac2(int number) {
def result = 0
number.downto(1) {
num -> result += num
}
result
}
闭包、集合和字符串
闭包常常用于列表、映射和字符串结合使用。例如遍历每一个元素
[11, 12, 13, 14].each {
print "${it} "
} // 11 12 13 14
["Java": 121, "Android": 122, "Groovy": 123].each {
println it
}
//Java=121
//Android=122
//Groovy=123
["Java": 121, "Android": 122, "Groovy": 123].each {
println "key is ${it.key}, value is ${it.value}"
}
//key is Java, value is 121
//key is Android, value is 122
//key is Groovy, value is 123
闭包的特性
由于闭包也是一个对象,所以可以作为方法的参数。
def list = [11, 12, 13, 14]
// 偶数
def isEven = { num -> return num % 2 == 0 }
// 奇数
def isOdd = { num -> return !isEven.call(num) }
// 根据传入的闭包过滤满足条件的元素
def filter(list, clos) {
return list.findAll(clos)
}
def evens = filter(list, isEven)
println "evens is ${evens}" // evens is [12, 14]
def odds = filter(list, isOdd)
println "odds is ${odds}" // odds is [11, 13]
闭包也可以作为另一个闭包的参数
def table = [11, 12, 13, 14, 15]
// 奇数
def isOdd = { num -> return num % 2 != 0 }
// 偶数
def isEven = { num -> return !isOdd.call(num) }
def filter = { clos, list ->
def result = []
for (element in list) {
if (clos.call(element)) {
// << 运算符追加新元素
result << element
} else {
result
}
}
return result
}
def odds = filter.call(isOdd, table)
println "odds id ${odds}" // odds id [11, 13, 15]
def evens = filter.call(isEven, table)
println "evens is ${evens}" // evens is [12, 14]
闭包作为返回值
// 返回闭包
def multiply(x) {
return { y -> x * y }
}
def result = multiply(2)
println "${result.call(3)}" // 6
闭包关键变量
Groovy闭包的强大之处在于它支持闭包方法的委托。Groovy的闭包有this、owner、delegate这三个属性,当在闭包内调用方法时,由它们来确定使用哪个对象来处理。
默认情况下delegate和owner是相等的,但是delegate是可以修改的,Gradle中的闭包很多功能都是通过修改delegate实现的。
下面讨论this、owner、delegate闭包三个重要变量:
this代表闭包定义处的类
owner代表闭包定义处的类或者对象
delegate代表任意对象,默认与owner一致
首先test.groovy脚本文件中定一个闭包,然后打印这三个关键字,发现打印的信息一样。
def scriptClouser = {
println "scriptClouser this: " + this
println "scriptClouser owner: " + owner
println "scriptClouser delegate: " + delegate
}
scriptClouser.call()
//scriptClouser this: com.kerwin.groovy.object.test@740cae06
//scriptClouser owner: com.kerwin.groovy.object.test@740cae06
//scriptClouser delegate: com.kerwin.groovy.object.test@740cae06
然后在test.groovy脚本文件中定义一个内部类Person,然后打印这三个关键字,static代表当前类,打印的信息也都一样。
class Person{
def static classClouser = {
println "classClouser this: " + this
println "classClouser owner: " + owner
println "classClouser delegate: " + delegate
}
// classClouser this: class com.kerwin.groovy.object.Person
// classClouser owner: class com.kerwin.groovy.object.Person
// classClouser delegate: class com.kerwin.groovy.object.Person
def static speech() {
def methodClassClouser = {
println "methodClassClouser this: " + this
println "methodClassClouser owner: " + owner
println "methodClassClouser delegate: " + delegate
}
// methodClassClouser this: class com.kerwin.groovy.object.Person
// methodClassClouser owner: class com.kerwin.groovy.object.Person
// methodClassClouser delegate: class com.kerwin.groovy.object.Person
methodClassClouser.call()
}
}
Person.classClouser.call()
Person.speech()
在test.groovy脚本文件中定义一个闭包,然后在闭包中定一个闭包,然后打印这三个关键字,发现打印信息不同。其实this就是这个构建脚本的上下文
def outerClouser = {
def innerClouser = {
println "innerClouser this: " + this
println "innerClouser owner: " + owner
println "innerClouser delegate: " + delegate
}
innerClouser.call()
}
//innerClouser this: com.kerwin.groovy.object.test@2a265ea9
//innerClouser owner: com.kerwin.groovy.object.test$_run_closure2@793f29ff
//innerClouser delegate: com.kerwin.groovy.object.test$_run_closure2@793f29ff
outerClouser.call()
// 如果修改innerClouser 闭包的delegate,打印信息
innerClouser.delegate = new Person()
//innerClouser this: com.kerwin.groovy.object.test@2a265ea9
//innerClouser owner: com.kerwin.groovy.object.test$_run_closure2@793f29ff
//innerClouser delegate: com.kerwin.groovy.object.Person@3e8c3cb
闭包委托策略
定义一个Student类
class Student {
def name
def printName = { "My name is ${name}" }
@Override
String toString() {
return printName.call()
}
}
定义Teacher类
class Teacher {
def name
}
设置student的printName闭包delegate为teacher, 闭包策略为DELEGATE_FIRST
DELEGATE_FIRST:首先代理类找,找不到从自己类找,自己类中找不到,抛出groovy.lang.MissingXXXException异常
DELEGATE_ONLY:只会在代理类找, 找不到抛出groovy.lang.MissingXXXException异常
def student = new Student(name: 'kerwin')
def teacher = new Teacher(name: 'CV')
student.printName.delegate = teacher
// 委托模式优先
student.printName.resolveStrategy = Closure.DELEGATE_FIRST
println student.toString() // My name is CV
在Gradle中,一般会指定delegate为当前的it,在闭包内就可以对该it进行配置,或者调用其方法
person {
println "this: ${this}" // this: com.kerwin.groovy.file.test@5a45133e
println "owner: ${owner}" // owner: com.kerwin.groovy.file.test@5a45133e
println "delegate: ${delegate}" // delegate: com.kerwin.groovy.Person@771a660
name = 'kerwin'
age = 23
printString() // printString: name is kerwin, age is 23
}
def person(Closure<Person> closure) {
def p = new Person()
println "${p}" // com.kerwin.groovy.Person@771a660
closure.delegate = p
// 委托模式优先
closure.resolveStrategy = Closure.DELEGATE_FIRST
closure.call(p)
}
class Person implements Serializable {
def name
def age
def printString() {
println "printString: name is ${name}, age is ${age}"
}
}
上例中,设置闭包的委托对象为当前创建的Person对象,并设置委托模式优先。在使用person方法创建一个Person的示例时,可以在闭包里直接对该Person实例配置。在Gradle中有很多类似的用法,基本上都是使用delegate的方式使用闭包进行配置。
流程控制语句
while语句、for语句、if语句、switch语句。使用方式跟Java一样,下面主要讲解与Groovy不同之处。
for语句
// 范围
for (count in 1..3) {
println "count: ${count}"
}
// List集合
for (count in [1, 2, 3]) {
println "count: ${count}"
}
//Map集合
for (entry in ["ker": 21, "john": 22]) {
println "key is ${entry.key}, value is ${entry.value}"
}
// 字符串
def result = []
for (letter in 'Groovy') {
result << letter
}
println result.toListString() // [G, r, o, o, v, y]
switch语句
def number = 1234
switch (number) {
case 111..115:
println "范围"
break
case [11, 12, 13, 14]:
println "集合"
break
case ~"[0-9]{4}":
println "正则表达式"
break
}
// 正则表达式
方法
在这里我主要讲解Groovy方法与Java的不同,然后能够看懂Gradle脚本里的代码就可以了。
括号可以省略不写
estMethod(1, 2)
// 括号可以省略不写
testMethod 1, 2
def testMethod(a, b) {
println "a + b = ${a + b}"
}
return可以不写的
Groovy中,定义有返回值的方法时,return语句是可以不写的。当不写return时,Groovy 会把方法执行过程中的最后一行代码作为返回值。
def result = testMethod 2, 3
println result
def testMethod(a, b) {
"a + b = ${a + b}"
}
闭包是可以作为参数传递的
// 比较呆板的写法
def list = [11, 12, 13, 14]
list.each({
println it
})
// Groovy规定,如果方法的最后一个参数是闭包,可以放到方法外面
list.each() {
println it
}
// 然后方法可以省略,就变成我们经常看到的样式
list.each {
println it
}
方法的默认参数
方法通常需要用关键字def声明。默认参数仅能出现在非默认参数之后。
def outInfo(params1, parames2 = 'Groovy') {
println "params1: ${params1}, parames2: ${parames2}"
}
outInfo("Hello", "Android") //params1: Hello, parames2: Android
// 如果没有传入第二个参数,就会使用默认值
outInfo("Hello") //params1: Hello, parames2: Groovy
Groovy中调用类方法逻辑,如下图展示:
类
类是创建对象实例的模板。首先定义一个简单描述银行的类,并添加两个属性。接下来创建这个类的对象和显示其属性值的代码。
class Account {
def id // 账号
def amount // 账户余额
}
//创建对象,使用命名参数模式
def account = new Account(id: "ABC123", amount: 349)
println "Account ${account.id} has amount ${account.amount}"
// Account ABC123 has amount 349
// 上面创建对象的方式等价于下面三行代码
// def account = new Account()
// account.setId("ABC123")
// account.setAmount(349)
def amount = account.getAmount()
println "${amount}"
上面示例隐藏大量信息。首先类Account中的两个属性拥有公共访问权限,其次account.amount所示的属性引用方法实际上是通过account.getAmount()实现的。也就是说,方法getter和方法setter都是Groovy类的隐含方法。
在类中可以定义方法,我们现在为Account类中添加存款和取款的方法。
class Account {
def id // 账号
def amount // 账户余额
// 存款
def add(number) {
amount += number
}
// 取款
def remove(number) {
amount -= number
return number
}
@Override
String toString() {
return "${id}:${amount}"
}
}
def account = new Account(id: "ABC123", amount: 10)
println account.toString() // ABC123:10
account.add(100) // 存100
println account.toString() // ABC123:110
account.remove(30) // 取出30
println account.toString() // ABC123:80
在java中,Account类需要有初始化对象的构造器方法,在Groovy中并不需要,只需new操作符和指定参数即可。但是Groovy也有构造器。
实例:设计银行系统建模。一个银行对应多个账户,使用映射处理一对多的关系。其中账户号作为key,Account作为值
class Bank {
// 银行名称
def name
// key : 账号 , value : 账户Account
def accounts = [:]
/**
* 打开或者创建账户
*/
def openOrCreateAccount(id, amount) {
def account = this.findAccount(id)
println account
if (account != null) {
account.add(amount)
return
}
account = new Account(id: id, amount: amount)
accounts[id] = account
}
/**
* 删除账户
*/
def deleteAccount(id) {
def account = findAccount(id)
if (account != null) accounts.remove(id)
}
/**
* 查找账户
*/
def findAccount(id) {
if (accounts.isEmpty()) return null
def acc = accounts.find {
entry -> return entry.key == id
}
return acc != null ? acc.value : null
}
/**
* 获取总资产
*/
def getTotalAssets() {
if (accounts.isEmpty()) return 0
def total = 0
accounts.each { id, account -> total += account.amount}
return total
}
}
元编程
接下来主要 介绍Groovy的元对象协议MOP(Mate-Object-Protocol)的组成部分,MOP是在运行时改变对象和类行为的系统的能力。
元类(MetaClass)的概念
在Groovy中,所有对象都实现了GroovyObject接口,声明在groovy.lang包中
public interface GroovyObject {
Object invokeMethod(String name, Object args);
Object getProperty(String propertyName);
void setProperty(String propertyName, Object newValue);
MetaClass getMetaClass();
void setMetaClass(MetaClass metaClass);
}
GroovyObject 与 MetaClass 协作,MetaClass 是Groovy元概念的核心,它提供了一个Groovy类的所有的元数据,也实现了接口MetaObjectProtocol:
Object invokeMethod(Object object, String methodName, Object[] arguments);
Object invokeMethod(Object object, String methodName, Object arguments);
Object invokeStaticMethod(Object object, String methodName, Object[] arguments);
Object invokeConstructor(Object[] arguments);
上面这些方法进行真正的方法调用,使用java反射,GroovyObject 的 invokeMethod方法默认实现转到相应的MetaClass 。MetaClass 被存储在MetaClassRegistry中,通过groovy也从MetaClassRegistry中获取MetaClass 。
方法调用和拦截
// MyMetaTest.groovy
class MyMetaTest {
def hello() {
return 'invoked hello directly'
}
@Override
Object invokeMethod(String name, Object args) {
return "invokeMethod: name:${name}, args:${args}"
}
}
MyMetaTest test = new MyMetaTest()
println test.hello() // invoked hello directly
println test.hello2('kerwin') // invokeMethod: name:hello2, args:[kerwin]
在Groovy中任何对象都实现GroovyObject接口,所以MyMetaTest 也默认实现了GroovyObject接口。如果调用MyMetaTest 中定义了的方法,如:hello,就会直接调用。如果调用MyMetaTest 中未定义方法,如:hello2,如果覆盖了invokeMethod就会执行invokeMethod方法,否则抛出MissingMethodException异常。
class MyMetaTest implements GroovyInterceptable{
def hello() {
return 'invoked hello directly'
}
@Override
Object invokeMethod(String name, Object args) {
return "invokeMethod: name:${name}, args:${args}"
}
}
MyMetaTest test = new MyMetaTest()
println test.hello() // invokeMethod: name:hello, args:[]
println test.hello2('kerwin') // invokeMethod: name:hello2, args:[kerwin]
println test.&hello() // invoked hello directly
让MyMetaTest 实现GroovyInterceptable接口,该接口是一个标记接口,没有任何方法需要实现。从这个接口的描述可知:实现该接口的类,类中的方法被调用时都会默认使用invokeMethod方法,不管该方法是否已经定义。如果要直接调用已定义的方法,需要使用.&操作符。
class MyMetaTest2 {
def greeting = 'accessed greeting directly'
@Override
void setProperty(String propertyName, Object newValue) {
println "setProperty>>>> propertyName:${propertyName}, newValue:${newValue}"
if ('greeting' == propertyName) {
greeting = newValue
}
}
@Override
Object getProperty(String propertyName) {
println "getProperty>>>> propertyName:${propertyName}"
if (propertyName == 'greeting') return greeting
return ""
}
}
MyMetaTest2 test2 = new MyMetaTest2()
println test2.greeting
//getProperty>>>> propertyName:greeting
//accessed greeting directly
test2.greeting = 'Hello Groovy, My is Kerwin.'
println test2.greeting // Hello Groovy, My is Kerwin.
//setProperty>>>> propertyName:greeting, newValue:Hello Groovy, My is Kerwin.
//getProperty>>>> propertyName:greeting
//Hello Groovy, My is Kerwin.
println test2.@greeting
// Hello Groovy, My is Kerwin.
该示例描述了属性的获取特性,如果想直接访问定义的属性,可以使用.@操作符, 对于类中不存在的属性,不要使用.@操作符,否则会抛出MissingFieldException异常。
metaClass
定义一个Person类
/**
* groovy中默认都是public
*/
class Person implements Serializable {
String name
Integer age
def increaseAge(Integer years) {
this.age += years
}
/**
* 一个方法找不到时,调用它代替
* @param name
* @param args
* @return
*/
def invokeMethod(String name, Object args) {
return "the method is ${name}, the params is ${args}"
}
def methodMissing(String name, Object args) {
return "the method ${name} is missing"
}
}
首先定义一个全局管理类ApplicationManager,通过metaClass为Person类注入一个静态方法createPersion
class ApplicationManager {
def static init() {
// 开启全局设置
ExpandoMetaClass.enableGlobally()
// 注入createPersion 静态方法
Person.metaClass.static.createPersion = { name, age ->
new Person(name: name, age: age)
}
}
}
调用Person注入的静态createPersion方法
class Entry {
static void main(def args) {
ApplicationManager.init()
Person person = Person.createPersion('kerwin', 123)
println "name: ${person.name}, age:${person.age}"
//name: kerwin, age:123
}
}
如果我的文章对您有帮助,不妨点个赞鼓励一下(^_^)
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。