前言
在上一篇文章中,主要讲解了Gradle基本介绍以及Groovy对应的基础语法,在这一篇中将会从如下几点继续深入Groovy。
- Groovy类与方法
- Groovy闭包
话不多说,直接开始。
1. Groovy类与方法
1.1 多种访问get/set方式
class Car {
def miles
private def year
def getMiles() {
println "getMiles"
return miles
}
private void setMiles(miles) {
println "setMiles"
this.miles = miles
}
}
def car = new Car()
car.miles = 20000
println car.miles+"\n\n"
car.@miles=10000
println car.@miles+"\n\n"
car.year = 2027
println car.year
这里没啥好说的,无非就是对类里面的成员变量执行了赋值与取值操作,但注意在这里我特意将对应setMiles方法以及year变量设置成了私有,接下来我们看运行效果
setMiles
getMiles
20000
10000
2027
我这为了区分看对应的作用,故意用了换行,从这个运行效果可知:
- 所有的变量默认是public的
- 如果要设置为私有禁止直接访问,仅申明private是不行的。依然可以使用’.'直接访问
- 默认会生成getter, setter方法 (参考year)
- 并且可以直接像使用成员变量的方法来自动判断调用getter/setter (参考miles)
- 当进行赋值时调用setter方法
- 当直接访问值时调用的是getter方法
- 使用’.@'才是真正的直接访问变量,跳过默认的getter/setter方法调用 (参考.@miles)
当然这是很标准的get/set访问的方式,除此之外还可以通过’.变量名’进行访问
def car = new Car()
car.@year=2021
car.@miles=30000
println car.'miles'+"\n\n"
println car.@'miles'+"\n\n"
def str = "year"
println car."$str"
运行效果
getMiles
30000
30000
2021
从这可知:通过’.变量名’进行访问依然会调用对应的getter/setter方法,因此通过这样的方式,代码可以更动态、灵活了!
1.2 构造方法
class Car {
def miles
private def year
def getMiles() {
println "getMiles"
return miles
}
private void setMiles(miles) {
println "setMiles"
this.miles = miles
}
}
def car = new Car(miles: 20000, year: 2021)
println(car.@miles+"\n\n")
car=new Car(miles: 30000)
println(car.@miles)
注意看,这里的 Car 里面并没有带参数的构造方法,但实例化Car的时候却强制指定对应的变量以及对应的值,我们看看运行后的效果是怎样的
setMiles
20000
setMiles
30000
从这个运行效果我们可知:
- 构造方法重载规则跟Java一样。
- 在构造方法中传入具名参数,但是要注意:传入的参数都是键值对,实则就是一个Map类型
- 这种方式传入的参数会自动拆解Map并且调用setter方法对应的进行赋值
这里我们看到当没有有参构造方法的时候,会自动拆解Map键值对,那么如果有参构造方法存在会是怎么回事呢?
class Car {
def miles
private def year
Car(){
}
Car(def miles){
this.miles=miles
}
Car(def miles,def year){
this.year=year
this.miles=miles
}
def getMiles() {
println "getMiles"
return miles
}
private void setMiles(miles) {
println "setMiles"
this.miles = miles
}
}
def car = new Car(miles: 20000, year: 2021)
println(car.@miles)
println car.@miles.getClass()
println "\n\n"
car=new Car(miles: 30000)
println(car.@miles)
println car.@miles.getClass()
println "\n\n"
car=new Car(40000)
println(car.@miles)
println car.@miles.getClass()
现在我们看到,我这吧所有构造方法全实现了,现在我们看看运行效果是怎样的。
[miles:20000, year:2021]
class java.util.LinkedHashMap
[miles:30000]
class java.util.LinkedHashMap
40000
class java.lang.Integer
这里我们可以看出:如果参数中还有非键值对的传参,就会把这些键值对当成Map了不会再进行自动拆解赋值。所以要有对应的构造方法才行。
1.3 操作符重载
学过Kotlin的小伙伴应该都清楚,任何对象都可以进行操作符重载,现在来看看Groovy的是怎样的
class Car {
def miles
private def year
Car(){
}
def plus(Car car){
this.year+car.year
}
}
def car=new Car()
car.@year=2020
def car2=new Car()
car2.@year=2021
println car+car2
来看看运行效果
Connected to the target VM, address: '127.0.0.1:57371', transport: 'socket'
4041
Process finished with exit code 0
这里我们看到,这里居然出现了对象+对象运算,按常理说这不是只有数字类型的对象才能这样写么?没错!在Groovy里面确实能够这样写,只不过需要实现对应操作符的重载方法。我这只举了一个经典的加法,我这把常用的贴出来,剩下的读者可以依次尝试。
2. Groovy闭包
Groovy中可以理解为闭包就是可执行的代码块,或匿名函数。闭包在使用上与函数与许多共通之处,但是闭包可以作为一个函数的参数。
2.1 普通闭包操作
def closure = {
a = 1, b = 2, c ->
println "a=$a b=$b c=$c"
}
closure(30)
println closure.class
运行效果
a=1 b=2 c=30
class com.zee.gradle.test01.GroovyTest02$_run_closure1
这个对于使用Kotlin的小伙伴来说完全可以说一眼就懂,这里用java代码解释就好比这样。
void closure(int c){
int a=1;
int b=2;
System.out.println("a="+a+" b="+b+" c="+c)
}
相信这样大家都应该能秒懂了。
2.2 柯里化闭包
在Groovy中,柯里化的概念只得到了部分应用。它不符合函数式编程中柯里化的真正概念,因为Groovy在闭包上应用了不同的作用域规则。Groovy中的柯里化将允许您设置闭包中一个参数的值,并且它将返回一个接受剩余参数的新闭包。
2.2.1 左柯里化
def content={int count,String str -> str*count}
def twice=content.curry(2)
println twice('a')=='aa'
println content(2,'a')==twice('a')
运行效果
true
true
在开头,定义了两个参数的闭包,当执行 content.curry(2) 代码时,此时的 twice 闭包就变成了
def twice={
int count=2,String str->str*count
}
此时调用twice方法就和上面普通闭包一样了。既然有左柯里化,那么肯定会有右柯里化,那么看看。
2.2.2 右柯里化
def content={int count,String str -> str*count}
def twice=content.rcurry('b')
println twice(3)=='bbb'
println twice(3)==content(3,'b')
这里可以看出,左柯里化以及右柯里化都是针对于两个参数的闭包而形成的,那么如果出现超过2个参数的闭包该怎么使用柯里化呢?
2.2.3 基于索引的柯里化
def volume={double a,double b,double c->a*b*c}
def fixedWidthVolume=volume.ncurry(1,2d)
println volume(3d,2d,4d)==fixedWidthVolume(3d,4d)
def fixedWidthAndHeight=volume.ncurry(1,2d,4d)
println volume(3d,2d,4d)==fixedWidthAndHeight(3d)
这里注释写的挺全的,看看运行效果
true
true
2.2.4 柯里化闭包总结
- 左右柯里化仅仅针对两个参数的闭包
- 使用左柯里化需要用curry关键方法
- 使用右柯里化需要用rcurry关键方法
- 对于多于两个参数的闭包,可以使用基于索引的柯里化
- 使用基于索引的柯里化需要用到ncurry关键方法
- ncurry方法第一个参数是对应开始的下标,第二个变量可多个,表示从哪开始赋值
2.3 闭包与接口/类进行转换
2.3.1 闭包可以作为方法的参数
def func3(closure2) {
closure2()
}
func3 {
println "call"
}
运行效果
call
这里先是定义了方法 func3,然后也写了对应 func3的 闭包代码,在方法里面调用了传入的闭包方法代码,也就是说,方法func3的方法参closure2就为下面func3的闭包代码,于是最终就运行了闭包里面的代码。
2.3.2 闭包与类进行转换
demo1
interface Action {
void call()
void call2()
}
Action closure1 = new Action() {
@Override
void call() {
println "call1"
}
@Override
void call2() {
println("call2")
}
}
def func2(closure3) {
closure3()
}
println closure1.class
func2(closure1)
运行效果
class com.zee.gradle.test01.GroovyTest02$1
call1
即使定义的方法传入的是闭包,但是如果传入的对象的类型也有call方法,那么,是可以执行这个对象的call方法的,实际上,闭包执行的也是call方法。
demo2
class Action {
void call(a) {
println "$a"
}
}
def a = new Action()
a(111)
运行效果
111
这里解释和上面一样:如果传入的对象的类型也有call方法,那么,是可以执行这个对象的call方法的,实际上,闭包执行的也是call方法
2.4 闭包重要的成员变量
private Object delegate;
private Object owner;
private Object thisObject;
private int resolveStrategy = OWNER_FIRST;
protected Class[] parameterTypes;
protected int maximumNumberOfParameters;
这分别用代码看看
def closure = {
int a, int b ->
a+b
}
closure(1, 2)
println closure.delegate
println closure.owner
println closure.thisObject
println closure.resolveStrategy
println closure.parameterTypes
println closure.maximumNumberOfParameters
运行效果
com.zee.gradle.test01.GroovyTest02@7dac3fd8
com.zee.gradle.test01.GroovyTest02@7dac3fd8
com.zee.gradle.test01.GroovyTest02@7dac3fd8
0
[int, int]
2
没啥好说的,就是一系列变量,但这些和对应闭包策略息息相关,接下来继续看闭包代理策略。
2.5 闭包中代理策略
class Test2 {
def func() {
println "Test2 func"
}
}
def func() {
println "Script func"
}
def closure = {
func()
}
closure()
closure.delegate = new Test2()
closure.resolveStrategy=Closure.DELEGATE_FIRST
closure()
这里首先有个闭包,闭包里面会调用 func 方法,然后在class内部和外部分别有对应的func方法,在设置策略前后都分别调用了对应的闭包。 现在closure.resolveStrategy选择DELEGATE_FIRST或者DELEGATE_ONLY 来看看效果
Script func
Test2 func
也就是说,当选择DELEGATE_FIRST或者DELEGATE_ONLY时,第一次调用设置过策略的闭包时,将会调用类里面的方法。
那看看选择 OWNER_FIRST 或者 OWNER_ONLY的效果
Script func
Script func
也就是说,当选择OWNER_FIRST 或者 OWNER_ONLY时,将会直接调用外部代码,类里面对应的方法也就失效了。
3. 结束语
好了,到这里,相信你对Groovy语法有了深一步的认识。在下一篇中,将会对Groovy的元编程进行详解。
|