join
该操作用给定的字符串连接list中元素的toString的值.例如,它在list的所有字符串元素中间插入了一个’^’分隔符.
['one', 'two', 'three'].join(‘^’)返回"one^two^three".
sort
该操作对list元素进行排序并创建一个新的list.排序可以接受用java.util.Comparator或闭包作为参数.
fruits = ['kiwi', 'strawberry', 'grape', 'banana']
// 下一行返回 [banana, grape, kiwi, strawberry].
sortedFruits = fruits.sort()
//下一行返回[kiwi, grape, banana, strawberry].
sortedFruits =
fruits.sort {l, r | return l.length() <=> r.length()}
上面的最后一个sort方法调用是把闭包作为方法参数的例子.在Groovy有很多方法可以做这件事.
Groovy Beans可以很容易的对多属性进行排序.假设有一个Player bean带有属性name, age和score.可以对这些bean的list players先基于age然后基于score进行排序.
players.sort { [it.age, it.score] }
min / max
这两个操作分别找出最小或最大的list元素或字符串字符.它们可以接受用java.util.Comparator或闭包作为参数. 例如, 它们找出一个list中最小和最大的数字.
[5, 9, 1, 6].min()返回1.
[5, 9, 1, 6].max()返回9.
reverse
该操作颠倒(反序)list中元素的位置或者字符串中字符的位置.
[1, 2, 3].reverse()返回[3, 2, 1].
Groovy重载了plus和minus运算符用于对java.util.List对象进行操作.
plus
该操作创建两个lists的一个合集,并且删去重复的元素.
[1, 2, 3] + [2, 3, 4]返回[1, 2, 3, 2, 3, 4].
minus
该操作删除第一个list中所有在第一个和第二个list都出现的元素.
[1, 2, 3, 4] – [2, 4, 6] 返回[1, 3].当list元素不是primitives,则使用 equals方法比较它们.
Groovy Maps
Groovy 的maps是java.util.HashMap类的实例. 他们可以使用方括号内部的逗号分隔的关键字/值对的list来创建. 关键字与值之间用冒号隔开. 例如,
players = ['baseball':'Albert Pujols',
'golf':'Tiger Woods']
println players['golf'] // 打印Tiger Woods
println players.golf // 打印Tiger Woods
for (player in players) {
println "${player.value} plays ${player.key}"
}
// 和前面的循环是一样的效果.
players.each {player |
println "${player.value} plays ${player.key}"
}
空的map可以使用 [:]创建.例如,
players = [:]
Groovy Switch
Groovy switch语句中可以使用任何对象包括类, List, Range和Pattern. Case语句使用isCase方法进行值的比较.这里提供了很多isCase方法的重载版本. 除非对于特定类型进行重载, 否则isCase使用equals方法.当一个case后面跟的是一个类名时, isCase使用instanceof. isCase方法在你的类中被重载.
下面是 switch语句对不同类型的值操作的例子.
switch (x) {
case ‘Mark’:
println "got my name"
break
case 3..7:
println ‘got a number in the range 3 to 7 inclusive’
break
case ['Moe', 'Larry', 'Curly']:
println ‘got a Stooge name’
break
case java.util.Date:
println ‘got a Date object’
break
case ~"\\d{5}":
println ‘got a zip code’
break
default:
println "got unexpected value ${x}"
}
Groovy Ranges
使用".."和"…"运算符创建Range.下面是一些例子.
3..7 创建一个range从3到7
3…7创建一个range从3到6
"A".."D"创建一个range从"A"到"D"
"A"…"D"创建一个range从"A"到"C"
Range是继承自java.util.AbstractList类并实现了groovy.lang.Range接口的类的对象.一个Range是一个不可修改的List. Range接口添加了getFrom和getTo方法来获得下界和上界的值. Range接口提供了两种实现. 当范围限制是整数值时使用groovy.lang.IntRange.它添加了一个contains 方法用于判断一个值是否在range内. 当范围限制是其他类型时使用groovy.lang.ObjectRange.它同样有contains方法,但是它仅仅只在作为范围限制的对象实现了java.lang.Comparable的时候有用.
Ranges 在循环中非常有用.参见下一部分的例子.
Groovy Looping
下面是在某一范围循环的6种方法.
for
for (i in 1..1000) { println i }
while
i = 1
while (i <= 1000) { println i; i++ }
each
(1..1000).each { println it }
times
1000.times { println it }
//循环从0到999的值
upto
1.upto(1000) { println it }
step
1.step(1001, 1) { println it }
//循环从1到1000的值;
// stopping one before the parameter value
List/Map/String方法接受闭包作为参数
一些List, Map和String方法接受闭包作为参数.
each
该操作用于遍历集合中的元素或者字符串中的字符.它为使用java.util.Iterator提供了另一种选择,可以得到更简单的代码.例如, 打印一个List中的每一个数
[5, 9, 1, 6].each {x | println x}
或
[5, 9, 1, 6].each {println it}
collect
该操作用于把一个集合或字符串转换成一个新的集合或字符串.例如,把List中的每个数都翻倍然后创建一个新的List
doubles = [5, 9, 1, 6].collect {x | x * 2}
把doubles赋值为[10, 18, 2, 12].
find
该操作用于找到第一个符合条件的集合元素或者字符串字符. 例如,找到list第一个大于5的数
[5, 9, 1, 6].find {x | x > 5}返回9.
findAll
该操作用于找到所有符合条件的集合元素或者字符串字符. 例如,找到list中所有大于5的数
[5, 9, 1, 6].findAll {x | x > 5} 返回 [9, 6].
every
该操作用于判断是否集合的每一个元素或者字符串的每一个字符都符合给定的条件. 例如, 判断List中所有的数是否都小于7
[5, 9, 1, 6].every {x | x < 7} 返回 false.
any
该操作用于判断是否存在集合中的元素或者字符串的字符符合给定的条件.例如,判断List中是否存在小于7的数
[5, 9, 1, 6].any {x | x < 7} 返回 true.
inject
该操作用于把值传给第一次遍历然后再把每次遍历的值作为参数传给下一次. 例如, 求5的阶乘(用一种不常见的方式)
factorial = [2, 3, 4, 5].inject(1) {
prevResult, x | prevResult * x
}
该闭包被执行了四次.
1) 1 * 2
2) 2 * 3
3) 6 * 4
4) 24 * 5
它返回120.
文件I/O
读取文件中的行(两种选择)
下面代码范例中的省略号 (…) 表示省略了的代码.
file = new File(‘myFile.txt’)
file.eachLine { println it }
lineList = file.readLines()
读取文件中的字节(两种选择)
file = new File(‘myFile.txt’)
file.eachByte { println it }
byteList = file.readBytes()
读取目录中的文件
dir = new File(‘directory-path’)
dir.eachFile { file | . . . }
读取文件并关闭资源
这些方法通过一个Reader或者InputStream读取文件并且保证最后会关闭资源不管是否出现了例外.
file.withReader { reader | . . . }
reader.withReader { reader | . . . }
inputStream.withStream { is | . . . }
写文件并关闭资源
这些方法通过一个Writer或OutputStream写文件并且保证最后会关闭资源不管是否出现了例外.
file.withWriter { writer | . . . }
file.withPrintWriter { pw | . . . }
file.withOutputStream { os | . . . }
writer.withWriter { writer | . . . }
outputStream.withStream { os | . . . }
’<<’运算符
添加到字符串
s = ‘foo’
s = s << ‘bar’
添加到一个StringBuffer
sb = new StringBuffer(‘foo’)
sb << ‘bar’
添加到list
colors = ['red', 'green']
colors << ‘blue’
写到数据流的末端
w = new File(‘myFile.txt’).newWriter()
w << ‘foo’ << ‘bar’
w.close()
对象导航(Object Navigation)
对象之间的关系(graphs)可以通过类似Xpath的语法使用点 (".")运算符来表现.为了避免产生NullPointerException例外, 使用运算符"->" 代替".".例如,
class Team {
String name
Person coach
players = []
}
class Person {
String name
}
p = new Person(name:’Mike Martz’)
t = new Team(name:’Rams’, coach:p)
//下一行打印和team.getCoach().getName()相同的内容.
println "coach = ${t.coach.name}"
t = new Team(name:’Blues’)
// 下一行返回空,
//不会抛出NullPointerException例外.
println "coach = ${t->coach->name}"
// 下一行抛出NullPointerException例外.
println "coach = ${t.coach.name}"
Groovy Reflection
假设你想通过一个对象得到一个类对象.在Java中使用someObject.getClass()完成这件事.在Groovy中,使用someObject.class完成.
假设你想通过一个类名得到一个类对象.在Java和Groovy中使用SomeClass.class或Class.forName("pkg.SomeClass")完成这件事.
打印Groovy类 Gstring所有方法的列表,
GString.class.methods.each { it.name }
打印Java接口java.util.List中所有方法的列表,
java.util.List.class.methods.each { it.name }
捕捉没有实现的方法
可以编写类来捕捉没有实现的方法.例如,
o = new CatchCall()
// The next line prints "unknown method Mark called with [19]".
println o.foo("Mark", 19)
class CatchCall {
invokeMethod(String name, Object args) {
try {
return metaClass.invokeMethod(this, name, args)
} catch (MissingMethodException e) {
//可以在这里插入
//处理特定方法和参数的特殊逻辑.
return "unknown method ${name} called with ${args}"
}
}
}
Groovy Markup
Groovy的Markup 使用invokeMethod方法来捕捉刚刚提到的那些不存在的方法并把它们转换为”结点” ("nodes"). 方法的参数被看作是结点的属性.方法后的闭包被看作是结点的内容(Parameters to the methods are treated as attributes of the nodes.Closures after the methods are treated as the content of the nodes. )它有和多作用包括
· 构建泛型,数据结构树 (NodeBuilder)
· 构建DOM树(DOMBuilder)
· firing SAX事件(SAXBuilder)
· 创建HTML或XML的字符串 (MarkupBuilder)
· 执行Ant任务 (AntBuilder)
· 创建Swing用户接口(SwingBuilder)
另外, 定制的builder可以通过继承groovy.util.BuilderSupport类来创建.
生成HTML
下面是使用 MarkupBuilder生成HTML的例子.
import groovy.xml.MarkupBuilder
mb = new MarkupBuilder()
mb.html() {
head() {
title("This is my title.")
}
body() {
p("This is my paragraph.")
}
}
println mb
这段代码生成了下面的HTML文件.
<html>
<head>
<title>This is my title.</title>
</head>
<body>
<p>This is my paragraph.</p>
</body>
</html>
生成XML
下面是使用MarkupBuilder生成XML的例子.
import groovy.xml.MarkupBuilder;
mb = new MarkupBuilder()
mb.autos() {
auto(year:2001, color:’blue’) {
make(‘ Toyota’)
model(‘Camry’)
}
}
println mb
这段代码生成了下面的XML文件.
<autos>
<auto year=’2001′ color=’blue’>
<make> Toyota</make>
<model>Camry</model>
</auto>
</autos>
Groovy SQL
Groovy使JDBC更简单. groovy.sql.Sql类提供了一个简单的方式来运行一个查询(query)以及遍历ResultSet中的的列.在下面的例子中, MusicCollection是一个数据库的名字(在本例中,已经被注册为一个ODBC 数据源), Artists是给数据库中一个表的名字,而Name则是该表中的列名.
import groovy.sql.Sql
dbURL = ‘jdbc:odbc:MusicCollection’
jdbcDriver = ’sun.jdbc.odbc.JdbcOdbcDriver’
sql = Sql.newInstance(dbURL, jdbcDriver)
sql.eachRow(’select * from Artists’) {
println it.Name
}
Groovlets
Groovlets可以拿来取代Servlets或JSP.它提供了以下的隐性变量(implicit variables).
· out –等同于HttpServletResponse.getWriter()方法
· request – 等同于 HttpServletRequest
· session -等同于HttpSession
下面是一个Groovlet例子.它能保存为一个名为SimpleGroovlet.groovy的文件.它使用"here-doc"来生成HTML文件.
out.println <<<EOS
<html>
<head>
<title>My Simple Groovlet</title>
</head>
<body>
<h1>My Simple Groovlet</h1>
<p>Today is ${new java.util.Date()}.</p>
</body>
</html>
EOS
GroovyServlet编译Groovlet并会为它们提供缓存加速(cache)直到它们的内容发生改变.当Groovlet改变了之后,GroovyServlet会自动重新编译Groovlet.GroovyServlet必须在web.xml中注册.
下面是web.xml文件的一个例子仅仅示范了注册GroovyServlet的部分.
<?xml version="1.0"?>
<!DOCTYPE web-app
PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd">
<web-app>
<servlet>
<servlet-name>Groovy</servlet-name>
<servlet-class>groovy.servlet.GroovyServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>Groovy</servlet-name>
<url-pattern>*.groovy</url-pattern>
</servlet-mapping>
</web-app>
Groovlets可以使用Ant来部署.基本步骤是
1. 使用下面的内容创建一个WAR.
o 上面的Groovlet源文件(*.groovy)
o WEB-INF 文件夹中的web.xml
o WEB-INF/lib文件夹中的 groovy*.jar 和asm*.jar文件
2. 在一个servlet 引擎如Tomcat中部署这个WAR.
下面是做这件事的Ant构造文件.
build.properties
build.dir=build
src.dir=src
# Directory that contains Groovlets
groovy.dir=${src.dir}/groovy
# Directory that contains web.xml
web.dir=${src.dir}/web
# Path to WAR that will be produced
war.file=${build.dir}/${ant.project.name}.war
# Where the WAR should be deployed
webapps.dir=${env.TOMCAT_HOME}/webapps
# JARs that must be in the WAR
asm.jar=${env.GROOVY_HOME}/lib/asm-1.4.1.jar
groovy.jar=${env.GROOVY_HOME}/lib/groovy-1.0-beta-4-snapshot.jar
build.xml
<project name="GroovletExample" default="deploy">
<property environment="env"/>
<property file="build.properties"/>
<target name="prepare">
<mkdir dir="${build.dir}"/>
</target>
<target name="war" depends="prepare"
description="creates WAR file">
<war destfile="${war.file}" webxml="${web.dir}/web.xml">
<fileset dir="${groovy.dir}"/>
<lib file="${groovy.jar}"/>
<lib file="${asm.jar}"/>
</war>
</target>
<target name="deploy" depends="war"
description="deploys WAR file">
<delete dir="${webapps.dir}/${ant.project.name}"/>
<delete file="${webapps.dir}/${war.file}"/>
<copy file="${war.file}" todir="${webapps.dir}"/>
</target>
</project>
在这个Groovlet例子被部署了之后, 你可以让它在你浏览器中显示出来通过访问如下的地址http://localhost:8080/GroovletExample/SimpleGroovlet.groovy. GroovletExample是网络应用程序的名字. SimpleGroovlet.groovy是Groovlet的名字.这是与web.xml文件中的GroovyServlet指定的url-pattern相对应的.
Issues
Groovy还不算完美.浏览关于Groovy的文章,请访问http://groovy.codehaus.org/ 并点击Issue Tracker连接.下面是一些发表了的文章以及其编号.
· Primitive parameters to methods and closures aren’t supported yet (128 & 133).
· Arrays of primitives aren’t supported yet (119).
· Static primitive fields aren’t supported yet (153).
· Chained assignment (x = y = 19) isn’t supported yet (57).
· * imports aren’t supported yet (84).
· Compiler doesn’t catch calls to non-existent methods on statically typed parameters (170).
· Nested classes aren’t supported yet (69).
总结
我们已经快速的浏览了Groovy的一些语法和特性.这些基于Java的捷径让你完成了更多的工作吗? 你在工作中找到了更多乐趣吗?你的代码是变得更容易理解还是更难了?我希望可以看到你们的反馈. 给我发Email :mark@ociweb.com. 同时你也可以在Groovy邮件列表中分享你的反馈在这里.