Groovy笔记

Groovy是什么

Groovy 是一种面向对象的动态类型语言,与 Java 一样运行在 JVM 上。

简介

Apache Groovy 是一个功能强大、可选类型动态语言,有静态类型静态编译能力。旨在通过简单、熟悉、易学的语法来提高 Java 平台的开发人员的开发效率。它与任何Java程序无缝集成,并可以为你的应用提供强大的功能,包括脚本能力、领域特定语言(Domain-Specific Language)、运行和编译时元编程函数式编程。

JDK和GDK之间的关系

GDK是基于JDK的,所以在Java代码和Groovy代码之间传递对象时,无需任何转换。当处在同一JVM中时,Java端和Groovy端使用的是同一对象

特点

Groovy还有其他一些使这门语言更为轻量级、更为易用的特性,试举几例如下:

语法示例

 

存在的陷阱

哪些情况下需指明类型

在使用Groovy编程时,我倾向于省略类型. 当然在必要的情况下,我也会选择指明类型。比如,

Groovy和Java混用

要在Groovy代码中使用Groovy类,无需做什么,直接就可以工作。我们只需要确保所依赖的类在类路径(classpath)下,要么是源代码,要么是字节码。要把一个Groovy脚本拉到我们的Groovy代码中,可以使用GroovyShell。而如果要在Java类中使用Groovy脚本,则可以使用JSR 223提供的ScriptEngine API。如果想在Java中使用Groovy类,或者想在Groovy中使用Java类,我们可以利用Groovy的联合编译(joint-compilation)工具

运行groovy

要运行Groovy代码,我们有两个选择。

在Groovy中使用Groovy类

要在Groovy代码中使用一个Groovy类,只需要确保该类在我们的classpath下。可以使用Groovy源代码,也可以把源代码编译成.class文件并使用该文件——随我们选择。当我们的Groovy代码引用了一个Groovy类时,Groovy会以该类的名字在我们的classpath下查找.groovy文件;如果找不到,则以同样的名字查找.class文件.

利用联合编译混合使用Groovy和Java

如果Groovy类是预先编译好的,那我们就可以方便地在Java中使用.class文件或JAR包。来自Java的字节码和来自Groovy的字节码,对Java而言没什么区别;我们必须把Groovy JAR文件放在我们的classpath下,类似于我们使用Spring、Hibernate或其他框架/类库的JAR文件时的做法。

如果我们只有Groovy源代码,而非字节码,又会怎样呢?请记住,当我们的Java类依赖其他Java类时,如果没有找到字节码,javac将编译它认为必要的任何Java类。不过javac对Groovy可没这么友好。幸好groovyc支持联合编译。当我们编译Groovy代码时,它会确定是否有任何需要编译的Java类,并负责编译它们。因此我们可以自由地在项目中混合使用Java代码和Groovy代码,而且不必执行单独的编译步骤。简单地调用groovyc就好。

要利用联合编译,我们需要使用-j编译标志。使用-J前缀把标志传给groovy编译器.

 

-Jsource 1.6会把可选的选项source = 1.6发送给编译器。使用javap检查生成的字节码,会发现JavaClass作为一个普通的Java类,扩展了java.lang.Object,而UseJavaClass扩展了groovy.lang.Script。

在Java中创建与传递Groovy闭包

如果仔细检查,我们会发现,当Groovy调用一个闭包时,它只是使用了一个名为call()的特殊方法。要在Java中创建一个闭包,我们只需要一个包含该方法的类。如果Groovy代码要向闭包传递实参,我们必须确保call()方法接受这些实参作为形参.

在Java中调用Groovy动态方法

每个Groovy对象都实现了GroovyObject接口,该接口有一个叫作invokeMethod()的特殊方法。该方法接受要调用的方法的名字,以及要传递的参数。在Java这一端,invokeMethod()可以用来调用Groovy中使用元编程动态定义的方法. 从Java中可以使用任何Groovy类,这点没有限制,不管它们是否是动态的。

在Groovy中使用Java类

在Groovy中使用Java类简单且直接。如果我们想使用的Java类是JDK的一部分,可以像在Java中那样导入这些类或它们的包。Groovy默认会导入很多包和类,因此,如果我们想使用的类已经导入(比如java.util.Date),直接用就可以了,无需再导入.

如果想使用一个自己的Java类,或者不是标准JDK中的类,在Groovy中可以像在Java中那样导入它们。请确保导入了必要的包或类,或者使用类的全限定名来引用它们。当运行groovy时,可以使用-classpath选项指定.class文件或JAR包的路径。如果类文件和我们的Groovy代码在同一目录下,则不需要通过该选项指定目录。

如果想显式地编译Groovy调用了java的代码,不必分别编译Java和Groovy。可以使用联合编译代替。

从Groovy中使用Groovy脚本

使用GroovyShell,我们可以在任何文件(或字符串)中对脚本调用evaluate()方法,以执行该脚本。

 

如果我们想向脚本传递一些参数,又该怎么办呢?我们可以使用一个Binding实例来绑定变量

 

在发起调用的脚本中,我们创建了一个变量name(与被调用脚本中用到的变量名字相同)。当创建GroovyShell的实例时,我们将当前的Binding对象传给了它(每个脚本执行时都有一个这样的对象)。被调用脚本现在就可以使用(读取和设置)发起调用脚本所知道的变量了.

如果不希望影响当前的binding, 而是想将其与被调用脚本的binding分开,我们只需要创建一个新的Binding实例,在该实例上调用setProperty()来设置变量名和值,并将其作为创建GroovyShell实例时的一个参数

 
从Java中使用Groovy脚本

如果想在Java中使用Groovy脚本,我们可以利用JSR 223(JSR即Java Specification Request,Java规范请求)需要确保…/jsr223-engines/groovy/build/groovy-engine.jar在我们的classpath下

MOP与元编程

元编程(meta programming)意味着编写能够操作程序的程序,包括操作程序自身。像Groovy这样的动态语言通过元对象协议(MetaObject Protocol,MOP)提供了这种能力。利用Groovy的MOP,创建类、编写单元测试和引入模拟对象都很容易。在Groovy中,使用MOP可以动态调用方法,甚至在运行时合成类和方法。借助MOP,在Groovy中可以创建内部的领域特定语言。

Groovy对象

Groovy对象是带有附加功能的Java对象。在一个Groovy应用中,我们会使用三类对象:POJO、POGO和Groovy拦截器。POJO(Plain Old Java Object)就是普通的Java对象,可以使用Java或JVM上的其他语言来创建。POGO(Plain Old Groovy Object)是用Groovy编写的对象,扩展了java.lang.Object,同时也实现了groovy.lang.GroovyObject接口。Groovy拦截器是扩展了GroovyInterceptable的Groovy对象,具有方法拦截功能。Groovy这样定义的GroovyObject接口:

 

我们可以通过调用setMetaClass()修改它的MetaClass。这给我们一种对象在运行时修改了它的类的感觉.

当我们调用一个方法时,Groovy会检查目标对象是一个POJO还是一个POGO. 对于一个POJO,Groovy会去应用类(application-wide)的MetaClassRegistry取它的MetaClass,并将方法调用委托给它。因此,我们在它的MetaClass上定义的任何拦截器或方法,都优先于POJO原来的方法.

对于一个POGO, 如果对象实现了GroovyInterceptable,那么所有的调用都会被路由给它的invokeMethod(), 在这个拦截器内,我们可以把调用路由给实际的方法,使类AOP(Aspect-Oriented Programming,面向方面编程)的操作成为可能. 如果该POGO没有实现GroovyInterceptable,那么Groovy会先查找它的MetaClass中的方法,之后,如果没有找到,则查找POGO自身上的方法。 如果该POGO没有这样的方法,Groovy会以方法名查找属性或字段。如果属性或字段是Closure类型的,Groovy会调用它,替代方法调用。如果Groovy没有找到这样的属性或字段,它会做最后两次尝试。如果POGO有一个名为methodMissing()的方法,则调用该方法。否则调用POGO的invokeMethod()。如果我们在POGO上实现了这个方法,它会被调用。invokeMethod()的默认实现会抛出一个MissingMethodException.