Groovy – Java的脚本语言

by
Mark Volkmann, Partner
Object Computing, Inc. (OCI)

                                                                   翻译:raxy

介绍

Groovy是用Java实现的开源脚本语言并且和它联系紧密.它需要JDK 1.4. GroovyJava添加了许多RubyPython脚本语言的特性. Groovy的特性包括动态类型(dynamic typing, 闭包(closures,简单对象导航( easy object navigation)和更加简洁的ListsMaps语法.所有这些特性和其他一些特性将会在本文中详细介绍.

这里引用Groovy网页上的话. "Groovy是设计用来以精简快速有趣的方式来在Java平台上处理事务,并且把类似Python Ruby等的强大功能带到Java的世界里."

Groovy脚本可以使用任何Java.它们可以被编译成Java字节码 (.class文件)并能被任何正常的Java类引用.Groovy编译器, groovyc,可以编译Groovy脚本文件和Java源文件,然而有些语法(例如内类)并不被支持.

理论上, 可以使用Groovy编写完整的应用程序,其具有和同等Java应用程序差不多的性能.这是Groovy和其他脚本语言不一样的如Ruby, Python, Perl以及BeanShell. 使Groovy现在运行得比Java还慢的原因之一是生成的字节码使用了映射(reflection)来调用构造函数以及私有/保护的方法.这个问题将会在以后的版本中解决.

Groovy是由James StrachanBob McWhirter创造的. James还参与了许多其他开源项目的开发,其中包括Jelly, dom4j, Jaxen, BetwixtMaven. BobJaxenDrools (一个开源的面向对象的JAVA规则引擎) 的创始人.

本文并没有涉及Groovy所有的特性,而只是涉及了其中的大部分.它假设你对Java的语法有足够的了解并能把JavaGroovy的语法进行比较.

如果我们能在好的编程语言使用什么样的语法的问题上达成一致的话那么我们就不需要那么多语言了.这样大量的程序设计语言会被排除在外,显然我们并不同意.Based on the number of programming languages out there, we obviously don't agree.)在读完本文之后,你可能认为Java的语法已经可以了并且认为Groovy的语法不太合你的口味.如果你的结论是这样,我鼓励你研究Pat Niemeyer BeanShell位于http://www.beanshell.org/.它更加接近于标准的Java语法.另一方面,如果你喜欢Groovy的简短的语法那么就一起groovy!

下载并安装Groovy

使用以下步骤可以下载Groovy.

1.    访问 http://groovy.codehaus.org/.

2.    点击下载顶部导航栏中的"Download".

3.    点击 "this site" 链接.

4.    选择一个版本下载.

CVS中可以下载最新的版本. 操作步骤的说明在这里可以找到.

要安装Groovy, 使用以下步骤.

1.    解压下载的文件.

2.    把环境变量GROOVY_HOME 设置到解压的目录.

3.    向环境变量PATH添加$GROOVY_HOME/bin (UNIX)%GROOVY_HOME%/bin (Windows).

运行Groovy

运行Groovy脚本有四种方式.在这些方式中,脚本会被解析, 转换成Java源代码以及编译成Java字节码(bytecode.

互动的Shell

命令groovysh启动了一个互动的shell,可以在那里输入Groovy语句.输入一些语句,在每个语句结尾按回车(enter)键. 语句不会被求值(evaluated)或执行,直到输入execute命令为止.

互动的Swing Console

groovyConsole命令会启动一个Swing窗口.在窗口的下半部分输入Groovy语句.Actions菜单中选择Run来运行他们.输出的内容在窗口的上半部分显示. 使用File 菜单中可以打开和保存脚本文件.


运行脚本文件

一个Groovy脚本文件 (扩展名为.groovy的文件)可以使用命令groovy script-name.groovy来运行.

运行编译好的脚本

使用命令groovyc script-name.groovy一个Groovy的脚本文件可以被编译为Java .class文件. 如果在脚本文件中使用了松散的语句(loose statements, .class文件中会包含一个main方法,这样它就可以象Java应用程序一样使用命令java script-name运行. Main方法的内容会在后面提到. classpath 必须包含Groovy lib目录中的groovy*.jarasm*.jar文件.

有一个定制的Ant任务完成这个任务!这个类是org.codehaus.groovy.ant.Groovyc.

一些语法细节

以下是JavaGroovy语法关键的不同之处.其他的我们会在后面谈到.

·它的目标是支持所有合法的Java语法, 但是这个目标暂时还未达到.

·每行语句结尾的分号是可有可无的.

·方法的参数旁边的圆括号也是可有可无的除非该方法没有参数或者是不加圆括号会造成歧义.但是,构造函数中的圆括号则是必需的.有些人更倾向于经常使用圆括号. 本文则倾向于在允许的情况下省略它.

·"return"语句在某些时候也是可有可无的.当一个方法要返回值的时候,如果执行到大括号(方法结尾的大括号)前的最后一条语句,那么就会把它的值作为返回值.以后Groovy可能会改成返回最后执行的那一条语句计算出来的值.

·Groovy 属性和方法默认的情况下是公开的(public,而不是象JAVA中是受保护的( protected. 稍后我们再讨论Groovy Beans.

·java.lang, groovy.langgroovy.util会被自动导入.

动态类型(Dynamic Typing

类型对于变量,属性,方法/闭包的参数(method/closure parameters)以及方法的返回类型都是可有可无的.他们都是在赋值给他们的时候才决定类型. 不同的类型会在后面用到.任何类型都可以被使用,即使是基本类型 (通过自动包装(autoboxing). 当需要时,很多类型之间的转换都会自动发生,比如在这些类型之间的转换: 字符串(String,基本类型(int) 和类型的包装类(type wrapper classes (Integer)之间.我们也可以把不同的基本类型添加到同一数组(collections)中.

Added Methods

Groovy向标准Java类添加了许多方法例如java.lang.Object java.lang.String.可以在http://groovy.codehaus.org/groovy-jdk.html找到那些添加的方法.其他的我们以后再谈.

dump

该操作返回如下形式的字符串<class-name@hashcode property-name=property-value ...>例如, <Car@ef5502 make=Toyota model=Camry>.

Printprintln

这两个静态方法打印对象的toString方法的值.例如, print carprintln car.

invokeMethod

这个操作使用映射(reflection)实现动态方法调用.语法格式是object.invokeMethod(method-name, argument-array).下面的例子会打印数值4.

    s = 'abcabc' // a java.lang.String

    method = 'indexOf'

    args = ['b', 2]

    println s.invokeMethod(method, args)

Groovy 字符串

字符串可以使用双引号也可以使用单引号. 当使用双引号的时候,可以在其中包含内嵌值(embedded values.包含内嵌值的语法格式是${expression}Ruby语言中差不多,只是这里使用了$Ruby中使用的是#.使用双引号的字符串其中包含了至少一个内嵌值,那么它就是groovy.lang.GString类的对象. 其他的字符串都是java.lang.String类的对象. 在需要的时候Gstrings会被强制自动转换为java.lang.String.

针对Groovy类如groovy.lang.GStringJavadoc可以在http://groovy.codehaus.org/apidocs/找到.

内嵌值对于实现toString方法很有帮助. 例如,

    String toString() { "${name} is ${age} years old." }

多行字符串可以用三种方式创建.下面的例子是等效的.最后一个例子使用了被称为 "here-doc"的方式. 在三个小于号之后,就是指定的分隔符字符串.字符串的值包括分隔符字符串出现的两次之间的所有字符. "EOS" (代表"End Of String"字符串的结尾的意思) 是个普通的分隔符,其他的分割符同样也可以使用.

    s = " This string

      spans three /"lines/"

      and contains two newlines."

 

    s = """ This string

      spans three "lines"

      and contains two newlines."""

 

    s = <<<EOS

      This string

      spans three "lines"

      and contains two newlines.

    EOS

注意:在前面的代码片段中最后一行的字符没有被保存在字符串中.

以下的方法被添加到java.lang.String类中.

contains

该操作判断一个字符串中是否含有给定的子串. 'Groovy'.contains('oo')则返回true.

count

该操作统计某子串在给定字符串中出现的次数. 'Groovy Tool'.count('oo')返回结果2.

tokenize

该操作使用给定的分隔符把字符串分割成令牌(token)并返回令牌(token)的集合.指定分隔符参数是可选的.默认的间隔符是空格. 'apple^banana^grape'.tokenize('^')返回结果['apple', 'banana', 'grape'].

minus

该操作会除去字符串中给定子串第一次出现的部分. 'Groovy Tool' - 'oo'返回'Grvy Tool'.

multiply

这个操作会把给定的字符串重复给定的次数 . 'Groovy' * 3 返回 'GroovyGroovyGroovy' .

正则表达式 (regex)

首先,我们来回顾一下J2SE 1.4对正则表达式的支持.那么我们来看看Groovy是怎么实现的.

J2SE 1.4, java.util.regex包中的类支持正则表达式. 模式(Pattern对象代表一个编译过的regex.它们是使用Pattern.compile("pattern")创建的. 该类的Javadoc描述了正则表达式的语法. Matcher 对象保存一个模式与一个字符串匹配的结果.它们是使用pattern.matcher("text")创建的.为了判断文本是否与模式想匹配, 我们使用matcher.matches(). 模式中的/必须有与之相对应的/.

Groovy3种方法可以支持正则表达式.
~"pattern"创建一个模式对象并等同于Pattern.compile("pattern").
"text" =~ "pattern"创建一个 Matcher对象并等同于Pattern.compile("pattern").matcher("text").
"text" ==~ "pattern"返回一个布尔值,等同于Pattern.compile("pattern").matcher("text").matches().更多的方法可以参见模式和Matcher javadoc.

例如,

    pattern = "//d{5}" // matches zip codes (5 digits)

    text = "63304" // a zip code

 

    println text ==~ pattern // prints "true"

 

    m = text =~ pattern

    println m.matches() // prints "true"

 

    // The next line requires a literal string for the pattern.

    // A variable can't used.

    p = ~"//d{5}"

    m = p.matcher(text)

    println m.matches() // prints "true"

Groovy脚本

Groovy的脚本文件通常是以".groovy"为后缀名的. 它们可以包含 (以任何顺序)松散语句(loose statements, 与类无联系的方法定义, 以及类定义.

例如,

    // These are loose statements.

    println 'loose statement'

    myMethod 'Mark', 19

    println new MyClass(a1:'Running', a2:26.2)

   

    // This is a method definition that

    // is not associated with a class.

    def myMethod(p1, p2) {

      println "myMethod: p1=${p1}, p2=${p2}"

    }

   

    // This is a definition of a class that

    // has two properties and one method.

    class MyClass {

      a1; a2

      String toString() { "MyClass: a1=${a1}, a2=${a2}" }

    }

方法和类的定义不需要放在它们被使用之前.在与类相关联的基本文件中松散方法会被编译为相应类中的静态方法.例如, Bar.groovy脚本中一个叫foo的松散方法会在类Bar中被编译成叫foo的静态方法.松散语句回被收集到run方法,由编译生成的main方法调用该方法

groovyc被用来便宜一个脚本的时候,生成的类会有一个包含了所有的松散语句的run方法以及调用run方法的main方法.

目前脚本还不能引用其他脚本中的代码除非他们是编译好的并被引入了的. 这会在不久之后改进.

运算符重载

Groovy支持一部分运算符的运算符重载.每个运算符对应于一个特定的方法. 只要在你的类中实现这些方法就使用相应的运算符调用这些方法对这些类的对象进行操作.方法可以被重载来对不同类型的参数进行操作.

比较运算符

a == b对应a.equals(b)
a != b对应!a.equals(b)
a === b对应Java中的a == b
a <=> b对应a.compareTo(b)
a > b对应a.compareTo(b) > 0
a >= b
对应a.compareTo(b) >= 0
a < b
对应a.compareTo(b) < 0
a <= b
对应a.compareTo(b) <= 0

比较运算符可以处理null值并且不会生成NullPointerException. Null被认为比任何值都小.

注意到在Groovy==运算符被用来判断两个对象是否具有相同的值而===运算符被用来判断它们是否是内存中的同一对象.

compareTo方法返回一个int值,如果a < b则返回小于0的值, 如果a > b则返回大于0的值, 如果a 等于 b则返回0.

其他运算符

a + b对应a.plus(b)
a - b对应a.minus(b)
a * b对应a.multiply(b)
a / b对应a.divide(b)
a++ and ++a对应a.increment(b)
a-- and --a对应a.decrement(b)
a[b]对应a.get(b)
a[b] = c对应a.put(b, c)

Groovy闭包

一个闭包就是可以使用参数的代码片段. 每个闭包会被编译成继承groovy.lang.Closure类的类.这个类有一个叫call方法,通过该方法我们可以传递参数并调用这个闭包.它们可以访问并修改在闭包创建的范围内的变量(They can access and modify variables that are in scope when the closure is created.)在闭包内创建的变量在闭包被调用的范围内同样可以被引用. 闭包可以保存在变量中并被作为参数传递到方法中.这在某些list, mapstring方法非常有用,我们会在以后提到.

定义闭包的语法格式是

    { comma-separated-parameter-list | statements }

例如,

    closure = { bill, tipPercentage | bill * tipPercentage / 100 }

    tip = closure.call(25.19, 15)

    tip = closure(25.19, 15) // 与上一行等效

传递了错误的参数数量会产生IncorrectClosureArgumentException.

关键字it用于只有一个参数的闭包.参数列表可以被省略而在语句中使用it代表参数.例如,下面的闭包是等效的.

    { x | println x }

    { println it }

下面是一个方法的例子,它使用了一个List和一个闭包作为参数.它被写成一个"松散方法",但是它同样可以被写成某个类的一个方法.方法遍历这个List, List中的每个项目调用闭包.它使用闭包返回true的项目生成一个新的List然后返回新的List. 注意Groovy提供的findfindAll方法可以替代它.

    def List myFind(List list, Closure closure) {

      List newList = []

      for (team in list) {

        if (closure.call team) newList.add team

      }

      newList

    }

下面是使用这个方法的例子.

    class Team { name; wins; losses }

   

    teams = []

    teams.add new Team(name:'Rams', wins:12 , losses:4)

    teams.add new Team(name:'Raiders', wins:4 , losses:12)

    teams.add new Team(name:'Packers', wins:10 , losses:6)

    teams.add new Team(name:'49ers', wins:7 , losses:9)

 

    winningTeams = myFind(teams) { it.wins > it.losses }

    winningTeams.each { println it.name }

没必要编写一个象myFind的方法因为List类中已经有一个findAll方法了.象下面这样使用它,

    winningTeams = teams.findAll { it.wins > it.losses }

 

Groovy Beans

这里是一个 Groovy Bean例子.

    class Car {

      String make

      String model

    }

这个类声明了两个属性,而不包含任何方法.然而,很多事情是在后台完成的. ,属性和方法默认是公共的(public.公共的和保护的(protected)属性会成为私有域但是它们的公共的/保护的get set 会被自动生成.Public and protected properties result in private fields for which public/protected get and set methods are automatically generated)这些都可以被重载来提供定制的行为.对于明确被声明为私有的(private)属性来说, getset 方法没有被生成.

上面的Groovy代码等同于以下的Java代码.

    public class Car {

        private String make;

        private String model;

     

        public String getMake() {

            return make;

        }

     

        public String getModel() {

            return model;

        }

     

        public void setMake(String make) {

            this.make = make;

        }

     

        public void setModel(String model) {

            this.model = model;

        }

    }

Groovy Beans生成的类继承了java.lang.Object并实现了groovy.lang.GroovyObject.它添加的方法有getProperty, setProperty, getMetaClass, setMetaClass以及invokeMethod. groovy.lang.MetaClass允许在运行时添加方法.

Groovy Beans可以使用有名参数创建.例如,下面的代码调用了Car类的无参数构造函数然后调用了每个独立属性的set方法.

    myCar = new Car(make:'Toyota', model:'Camry')

Groovy Lists

Groovy listsjava.util.ArrayList类的实例.它们可以使用方括号内部的逗号分隔的值表来创建. 例如,

    cars = [new Car(make:'Honda', model:'Odyssey'),

            new Car(make:'Toyota', model:'Camry')]

 

    println cars[1] //指的是Camry

 

    for (car in cars) { println car } //调用CartoString方法

 

    class Car {

      make; model

      String toString() { "Car: make=${make}, model=${model}" }

    }

为了确定从list的末端开始计算的list的元素的位置,我们使用负数索引(negative indexes.

空的lists可以用[]创建.例如,

    cars = []

有两种方式添加元素.

    cars.add car

    cars << car

List可以使用数组调用array.toList()方法创建.数组同样可以使用list调用list.toArray()方法创建.

Groovy还向java.util.List添加了一些方法.

count

该操作计算list中有多少个元素与给定的对象相等.
[1, 2, 3, 1].count(1)返回2.

immutable

该操作使用java.util.Collections类的静态方法unmodifiableList创建一个集合的不能修改的拷贝.例如,

      list = [1, 2, 3].immutable()

      list.add 4 //抛出java.lang.UnsupportedOperationException异常

intersect

该操作创建一个含有两个给定list的公共元素的list.
[1, 2, 3, 4].intersect([2, 4, 6])返回[2, 4] 

 

 

 

 

 

 

 

 

 

 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值