《Groovy程序设计》读书笔记
2017-02-20 / modified at 2022-04-04 / 1.3k words / 5 mins
️This article has been over 2 years since the last update.

其实这本书早就买了,本来是作为字典查知识点的,现在又读了一遍。使用Groovy已经有了接近一年的时间,再读一遍。本文就是一些总结。

1. Groovy学习路线

Groovy在网上还是比较冷,如果读者不喜欢Groovy,项目没有使用或者对DSL没有兴趣的话,可以去试一下kotlin

1.1. 学习流程

  • 直接找一本书开始看,比如本文的书
  • 看了一半,使用GroovyConsole工具去实验
  • 使用Groovy代替Java去写部分代码
  • 最终重构Java代码,它将有强烈的Groovy风格

1.2. 学习工具建议

  • 使用Idea作为开发工具,它可以实现类型推导,并且反编译看Groovyc生成的Java也非常方便
  • 特别推荐使用调试时的Code Fragment
  • 加入CompileStatic注解,实现了Groovy静态化,方便对比编译结果

2. Groovy部分知识点

由于学的语言比较多,因此只说下Groovy的重点特性

2.1. 操作符重载

默认的各种重载通过DefaultGroovyMethods实现,这个C++,Scala等语言也是支持的。工具类代码难度不高,可读性也没问题,类似于Guava。

2.1.1. boolean值重载: asBoolean()

测试代码如下

1
2
3
4
5
6
def a = []
if(a){
println "not null"
} else {
println 'null'
}

可以发现它的调用栈是如下的,也就是说,在Groovy中,if要求返回值必须为boolean,所有数据最终都会调用asBoolean()

1
2
3
4
org.codehaus.groovy.runtime.DefaultGroovyMethods.asBoolean(DefaultGroovyMethods.java:10390)
org.codehaus.groovy.runtime.typehandling.DefaultTypeTransformation.castToBoolean(DefaultTypeTransformation.java:185)
org.codehaus.groovy.runtime.typehandling.DefaultTypeTransformation.booleanUnbox(DefaultTypeTransformation.java:74)
test.run(test.groovy:2)

因此在Groovy中,不需要写if(s== null || s.isEmpty())这样的代码,而是直接用if(s)即可

这里有个坑,比如if(0)就返回了false,而很多时候0是有意义的,导致费用等业务出现逻辑错误。

2.1.2. +重载: plus()

这个API过于灵巧了,比如List相加的时候。我个人建议用plus代替+,因为时间久了这些语法糖可能会忘记

你可以借助IDE的类型推导,按住Ctrl点击代码的加号,就可以进入相应的源码中

2.1.3. 其它操作符重载

这些用的不多,自己点进去看吧

  • forIn重载: next()
  • <<重载: leftShift()
  • a[‘b’]属性获取: a.getAt(‘b’)

2.2. 集合类

Groovy的集合类API几乎没有任何门槛,比如each, find, collect 等,这种API一次学习,各个平台都可以使用。实现类同样在DefaultGroovyMethods中,这些API必须精通至少一个平台

这类代码都推荐看,类似还有Java8的Stream, Google的Guava, 都是惰性求值。有了此类代码的思路,以后遇到新语言直接看DOC即可。

2.3. 使用Groovy设计DSL

DSL是领域专属语言,使用Groovy开发DSL有如下优点

  • 相对应Java,性能差距并不是非常大,同样享受到了JVM红利,并且也支持静态编译。
  • 相对应XML等外部DSL,信息量比标记语言更大(想想ant与gradle的区别),而且它的methodInvoking机制非常方便定制
  • 相比于自己折腾一个新语言,它不用实现复杂的Parser与断点/IDE,避免了内部派系之争

举个例子,有的老项目采用了ant脚本进行打包,调试费力不说,跨平台换个jar包也一堆问题,这时我们可以用Groovy+Jenkens去代替它

比如配置一个maven任务,可以是下面这样的,源码见这里

1
2
3
4
5
6
7
8
9
10
11
mavenJob('example') {
logRotator(-1, 10)
jdk('Java 7')
scm {
github('jenkinsci/jenkins', 'master')
}
triggers {
githubPush()
}
goals('clean verify')
}

如果你熟悉Jenkins,那么上面的代码几乎可以秒懂,甚至一个Groovy外行也可以修改定制。

单行代码中的信息量表达能力越高,它越仅适用于某些特定业务——它背后的解释器专为这些语法进行定制。

2.3.1. Closure、Lambda and Functional Programming

闭包是短小的、轻量的匿名函数,在动态语言中使用非常广泛。它最开始从函数式编程的Lambda表达式中派生,指定了一个函数的参数与映射,并加入了语法糖。在实际开发中,闭包一般用于

  • 封装业务无关的样板代码(比如资源清理,网络连接、简化循环),值得注意的是,闭包多作为参数传入函数,而工具类是作为主动方去调用的。使用Groovy的Category可以无侵入使用Java之前的工具类
  • 创建内部DSL,比如Groovy的XmlMarkup

2.3.2. 闭包与函数的区别

  1. 函数入参可以把闭包代码段作为入参传入
  2. 闭包是匿名函数。函数用于解决特定领域问题,闭包着重于解决通用领域问题
  3. 函数与闭包内部都尽量避免与外界接触,最好是“无状态”的,比如Java的匿名内部类就强制外部变量为final

REFFERENCE

  1. 集合类工具Guava与惰性求值
  2. 函数式编程RxJava操作实例
  3. https://github.com/shekhargulati/java8-the-missing-tutorial
  4. 《Java8函数式编程》