2016年 2月 11日
春节假期学习簿之Groovy学习笔记之语法浅析
这个春节过得很充实,因为学习了Groovy。“了”字用得不对,因为还在学习中。趁翔还是热得,稍做些总结。古人有云,学而时习之,不亦乐乎嘛。
Groovy与Java
如果你想要学好Groovy,你首先得是一位有经验的Java程序员。Groovy是Java程序员的出路(之一)。单独来看的话,太小众。
Groovy比Java更动态,更偏向于函数化。它支持不可变对象和闭包,但同时又没有抛弃指令式编程。它不强制你使用何种风格,所以选择权在你手上。因此,个人觉得,它更像Java程序员的出路,而不是一门独立的函数化编程语言。
动态类型与多路分发
与Java不同,Groovy是动态的。说它是动态的,是因为它是运行期多路分发/多宗绑定/多分派的(Multiple Dispatch/Multimethods 下文称多宗绑定)。
所谓多宗绑定既:注1
- 方法的接收者与方法的参数统称为宗量。
- 根据绑定基于多少种宗量,将绑定划分为单宗绑定和多宗绑定两种。
- 多宗绑定既基于多种宗量对目标方法进行选择。
- Java是,编译期多分派,运行期单宗绑定的。
我知道你看不懂,来来来,我们来看几段代码:
等同于Java中的
但是,如果你在Java中
资深Java程序员会这样告诉你
someMethod with object param
someMethod with string param
但如果你在Groovy中做同样的事情的话
你会得到这样的结果
someMethod with string param
someMethod with string param
我知道你在想Groovy是怎么实现的,让我来告诉你。
首先,Groovy有类型猜测系统(Type inference),编译期会根据类型猜测系统的提示进行方法选择。
其次,所有Groovy的对象都有元类信息(metaClass),运行期通过把所有方法的调用分发至MOP(Meta Object Protocol,元对象协议)上来进行动态地方法选择。简单来说,有点像运行期做了个AOP,然后反射去拿各种对象信息,再去调用正确的方法。(不负责非正确比喻,请无视。当然Groovy肯定不是用AOP和反射来实现的,并且它的做法比反射理论上来说更高效)
为什么Groovy能做到这些?因为它和Java是异源同宗的,虽然两者最终编译结果都是能够在JVM上运行的class字节码,但是groovy并非先编译成Java再编译成class字节码的,所以它可以绕过Java的编译器,做一些Java做不到但是JVM能做到的事情。
譬如说像下面这种会让Java程序员抓狂的代码
输出
Hello World from an Object!
你看!一个方法凭空就加到Object对象上惹!
函数化编程
作为一门现代化编程语言,不支持函数化编程是不行的。Groovy支持函数化编程的两大特性:
- 方法(闭包)可以作为头等公民来传递。
- 支持不可变对象(immutable)和等值(equality)而非等同(identity)比较
可传递的方法(闭包)
我们还是来看代码吧
这还不是最赞的地方,还有更赞的
你看这个闭包没有继承Runnable也没有实现run方法!如果将这样的闭包用在像addListener这样的方法上将会有多爽快!是的,你的确可以这样用!
当然,除了闭包,方法也可以传递,但是由于Java的限制,你得用&
创建一个方法闭包:
说到闭包就不得不谈到闭包的作用域scope,在所有函数式编程语言总,这是一个很高级的话题,一旦谈到这个问题,不外乎难倒面试/教做人/欺负新人/装逼的。为去歧义,明确说一下,这里的“闭包的作用域”指的不是闭包本身的作用域,而是指闭包代码块内各个变量的作用域。
在Groovy中,闭包的作用域是可以通过更改delegate来改变的。闭包代码块内,所有变量的引用,都是通过变量前加前缀delegate.
来解析的,而delegate本身所指向的对象是可以改变的,默认指向的是闭包的所有者(owner),即定义闭包的对象。
并且,“变量前加前缀delegate.
”这个操作,即闭包内变量的解析,也是可以通过为闭包设置resolveStrategy
属性来改变的。可用的resolveStrategy
有OWNER_ONLY
, OWNER_FIRST
(默认), DELEGATE_ONLY
, DELEGATE_FIRST
, SELF_ONLY
。这样Groovy闭包内作用域就变得非常灵活可变了。
不可变对象和等值比较
Groovy语法本身没有对这个特性提供特别的支持,它是通过提供注解和覆盖Java的比较方法来实现的。
考虑下面这段Java代码
结果如下
Java中双等比较的是两个变量是否是同一个引用,而想要做值比较的话,就得用equals,如果是自己写得bean的话,就得像上面这样写许多代码。
再考虑下面这段java代码
结果如下
像上面这样的代码,一般是用来为难面试的和实习生同学的,因为这样的不一致导致很多刚入门的同学头昏眼花的。
Groovy中的比较就很直接了
结果如下
回到前面一个例子中来,在Groovy中,想要自己写一个bean并且做等值比较,那是相当的爽快
结果如下
重点就在于@groovy.transform.Immutable
这个注解上,你可以尝试一下删除这个注解,然后看看运行的结果会有什么变化。
好奇的同学肯定会问这是怎么实现的?答案就是AST Transformation(语义树变换)。眼熟的同学肯定会说,我看到了lombok。
是的,神奇之处就在于此,其实Java也有同样的能力(lombok)。Groovy自带了很多AST变换的功能,不仅如此,还提供了可以一些附加工具,让程序员可以很方便地访问AST节点来做编译期检查甚至改变语法树的强大功能。
这些都是很高级的功能了,就不在此展开了。(其实是我还没掌握)
总结
这里挑选了Groovy的几个特性做了简介,但这只是强大Groovy的一部分功能,而且都是很抽象的理论上的特性,下次再记录一些比较实用的功能会比较好一点。
注1: 参考《深入理解Java虚拟机-JVM高级特性与最佳实践》,周志明著,2011年9月版,8.3.2.分派,209页。