﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>BlogJava-黑灵客栈-随笔分类-脚本语言</title><link>http://www.blogjava.net/mstar/category/1654.html</link><description>搞软件开发就像被强奸,如果不能反抗,就享受它吧！</description><language>zh-cn</language><lastBuildDate>Fri, 06 Nov 2009 00:36:28 GMT</lastBuildDate><pubDate>Fri, 06 Nov 2009 00:36:28 GMT</pubDate><ttl>60</ttl><item><title>Tomcat里的cpappend.bat </title><link>http://www.blogjava.net/mstar/archive/2009/11/05/301210.html</link><dc:creator>黑灵</dc:creator><author>黑灵</author><pubDate>Thu, 05 Nov 2009 01:43:00 GMT</pubDate><guid>http://www.blogjava.net/mstar/archive/2009/11/05/301210.html</guid><wfw:comment>http://www.blogjava.net/mstar/comments/301210.html</wfw:comment><comments>http://www.blogjava.net/mstar/archive/2009/11/05/301210.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.blogjava.net/mstar/comments/commentRss/301210.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/mstar/services/trackbacks/301210.html</trackback:ping><description><![CDATA[在Tomcat的发行包中有一个bat文件很有意思.<br />
可以把指定的参数添加到classpath里面.<br />
和bat脚本for一起配合,可以实现自动的把某个目录下的jar包都放到classpath里.<br />
<br />
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #000000;">rem&nbsp;</span><span style="color: #000000;">---------------------------------------------------------------------------</span><span style="color: #000000;"><br />
rem&nbsp;Append&nbsp;to&nbsp;CLASSPATH<br />
rem<br />
rem&nbsp;$Id:&nbsp;cpappend.bat&nbsp;</span><span style="color: #000000;">301115</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">2002</span><span style="color: #000000;">-</span><span style="color: #000000;">08</span><span style="color: #000000;">-</span><span style="color: #000000;">04</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">18</span><span style="color: #000000;">:</span><span style="color: #000000;">19</span><span style="color: #000000;">:43Z&nbsp;patrickl&nbsp;$<br />
rem&nbsp;</span><span style="color: #000000;">---------------------------------------------------------------------------</span><span style="color: #000000;"><br />
<br />
rem&nbsp;Process&nbsp;the&nbsp;first&nbsp;argument<br />
</span><span style="color: #0000ff;">if</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">""</span><span style="color: #000000;">%</span><span style="color: #000000;">1</span><span style="color: #000000;">""</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">==</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">""""</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">goto</span><span style="color: #000000;">&nbsp;end<br />
set&nbsp;CLASSPATH</span><span style="color: #000000;">=%</span><span style="color: #000000;">CLASSPATH</span><span style="color: #000000;">%</span><span style="color: #000000;">;</span><span style="color: #000000;">%</span><span style="color: #000000;">1</span><span style="color: #000000;"><br />
shift<br />
<br />
rem&nbsp;Process&nbsp;the&nbsp;remaining&nbsp;arguments<br />
:setArgs<br />
</span><span style="color: #0000ff;">if</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">""</span><span style="color: #000000;">%</span><span style="color: #000000;">1</span><span style="color: #000000;">""</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">==</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">""""</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">goto</span><span style="color: #000000;">&nbsp;doneSetArgs<br />
set&nbsp;CLASSPATH</span><span style="color: #000000;">=%</span><span style="color: #000000;">CLASSPATH</span><span style="color: #000000;">%</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">%</span><span style="color: #000000;">1</span><span style="color: #000000;"><br />
shift<br />
</span><span style="color: #0000ff;">goto</span><span style="color: #000000;">&nbsp;setArgs<br />
:doneSetArgs<br />
:end</span></div>
<br />
<strong>配合for一起用<br />
<br />
</strong>
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #000000;">set&nbsp;CURRENT_DIR</span><span style="color: #000000;">=%</span><span style="color: #000000;">cd</span><span style="color: #000000;">%</span><span style="color: #000000;"><br />
set&nbsp;CLASSPATH</span><span style="color: #000000;">=</span><span style="color: #000000;">.<br />
</span><span style="color: #0000ff;">for</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">%%</span><span style="color: #000000;">i&nbsp;in&nbsp;(</span><span style="color: #000000;">%</span><span style="color: #000000;">CURRENT_DIR</span><span style="color: #000000;">%</span><span style="color: #000000;">\lib\</span><span style="color: #000000;">*</span><span style="color: #000000;">.jar)&nbsp;</span><span style="color: #0000ff;">do</span><span style="color: #000000;">&nbsp;call&nbsp;cpappend.bat&nbsp;</span><span style="color: #000000;">%%</span><span style="color: #000000;">i<br />
start&nbsp;java&nbsp;</span><span style="color: #000000;">-</span><span style="color: #000000;">Duser.dir</span><span style="color: #000000;">=%</span><span style="color: #000000;">CURRENT_DIR</span><span style="color: #000000;">%</span><span style="color: #000000;">&nbsp;&nbsp;</span><span style="color: #000000;">-</span><span style="color: #000000;">cp&nbsp;</span><span style="color: #000000;">%</span><span style="color: #000000;">CLASSPATH</span><span style="color: #000000;">%</span><span style="color: #000000;">&nbsp;a.b.c.MainApp</span></div>
<br />
<img src ="http://www.blogjava.net/mstar/aggbug/301210.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/mstar/" target="_blank">黑灵</a> 2009-11-05 09:43 <a href="http://www.blogjava.net/mstar/archive/2009/11/05/301210.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>显示文件列表的groovy脚本</title><link>http://www.blogjava.net/mstar/archive/2009/09/27/296690.html</link><dc:creator>黑灵</dc:creator><author>黑灵</author><pubDate>Sun, 27 Sep 2009 11:57:00 GMT</pubDate><guid>http://www.blogjava.net/mstar/archive/2009/09/27/296690.html</guid><wfw:comment>http://www.blogjava.net/mstar/comments/296690.html</wfw:comment><comments>http://www.blogjava.net/mstar/archive/2009/09/27/296690.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/mstar/comments/commentRss/296690.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/mstar/services/trackbacks/296690.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 显示文件列表的groovy脚本&nbsp;&nbsp;<a href='http://www.blogjava.net/mstar/archive/2009/09/27/296690.html'>阅读全文</a><img src ="http://www.blogjava.net/mstar/aggbug/296690.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/mstar/" target="_blank">黑灵</a> 2009-09-27 19:57 <a href="http://www.blogjava.net/mstar/archive/2009/09/27/296690.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[ZZ]Groovy Goodness: Using Lists and Maps As Constructors</title><link>http://www.blogjava.net/mstar/archive/2009/09/08/294334.html</link><dc:creator>黑灵</dc:creator><author>黑灵</author><pubDate>Tue, 08 Sep 2009 09:09:00 GMT</pubDate><guid>http://www.blogjava.net/mstar/archive/2009/09/08/294334.html</guid><wfw:comment>http://www.blogjava.net/mstar/comments/294334.html</wfw:comment><comments>http://www.blogjava.net/mstar/archive/2009/09/08/294334.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/mstar/comments/commentRss/294334.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/mstar/services/trackbacks/294334.html</trackback:ping><description><![CDATA[这个NB~~~！<br />
<br />
Constructors in Groovy can be invoked in a classic Java way, but we can
also use lists or maps to create objects. Groovy supports an explicit
coersion of a list to a constructor with the <code>as</code> keyword.
Or we can rely on the implicit coersion when Groovy looks at the type
of the variable to automatically convert the list to the right
constructor call.<br />
<br />
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #008000;">//</span><span style="color: #008000;">&nbsp;Default&nbsp;constructor&nbsp;invocation:</span><span style="color: #008000;"><br />
</span><span style="color: #000000;">def&nbsp;url1&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;URL(</span><span style="color: #000000;">'</span><span style="color: #000000;">http</span><span style="color: #000000;">'</span><span style="color: #000000;">,&nbsp;</span><span style="color: #000000;">'</span><span style="color: #000000;">www.mrhaki.com</span><span style="color: #000000;">'</span><span style="color: #000000;">,&nbsp;</span><span style="color: #000000;">80</span><span style="color: #000000;">,&nbsp;</span><span style="color: #000000;">'</span><span style="color: #000000;">/</span><span style="color: #000000;">'</span><span style="color: #000000;">)<br />
</span><span style="color: #0000ff;">assert</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">'</span><span style="color: #000000;">http</span><span style="color: #000000;">'</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">==</span><span style="color: #000000;">&nbsp;url1.protocol<br />
</span><span style="color: #0000ff;">assert</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">'</span><span style="color: #000000;">www.mrhaki.com</span><span style="color: #000000;">'</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">==</span><span style="color: #000000;">&nbsp;url1.host<br />
</span><span style="color: #0000ff;">assert</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">80</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">==</span><span style="color: #000000;">&nbsp;url1.port<br />
</span><span style="color: #0000ff;">assert</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">'</span><span style="color: #000000;">/</span><span style="color: #000000;">'</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">==</span><span style="color: #000000;">&nbsp;url1.file<br />
</span><span style="color: #0000ff;">assert</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">'</span><span style="color: #000000;">/</span><span style="color: #000000;">'</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">==</span><span style="color: #000000;">&nbsp;url1.path<br />
<br />
</span><span style="color: #008000;">//</span><span style="color: #008000;">&nbsp;Explicit&nbsp;coersion&nbsp;with&nbsp;as&nbsp;keyword:</span><span style="color: #008000;"><br />
</span><span style="color: #000000;">def&nbsp;url2&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;[</span><span style="color: #000000;">'</span><span style="color: #000000;">http</span><span style="color: #000000;">'</span><span style="color: #000000;">,&nbsp;</span><span style="color: #000000;">'</span><span style="color: #000000;">www.mrhaki.com</span><span style="color: #000000;">'</span><span style="color: #000000;">,&nbsp;</span><span style="color: #000000;">80</span><span style="color: #000000;">,&nbsp;</span><span style="color: #000000;">'</span><span style="color: #000000;">/</span><span style="color: #000000;">'</span><span style="color: #000000;">]&nbsp;as&nbsp;URL<br />
</span><span style="color: #0000ff;">assert</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">'</span><span style="color: #000000;">http</span><span style="color: #000000;">'</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">==</span><span style="color: #000000;">&nbsp;url1.protocol<br />
</span><span style="color: #0000ff;">assert</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">'</span><span style="color: #000000;">www.mrhaki.com</span><span style="color: #000000;">'</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">==</span><span style="color: #000000;">&nbsp;url2.host<br />
</span><span style="color: #0000ff;">assert</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">80</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">==</span><span style="color: #000000;">&nbsp;url2.port<br />
</span><span style="color: #0000ff;">assert</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">'</span><span style="color: #000000;">/</span><span style="color: #000000;">'</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">==</span><span style="color: #000000;">&nbsp;url2.file<br />
</span><span style="color: #0000ff;">assert</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">'</span><span style="color: #000000;">/</span><span style="color: #000000;">'</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">==</span><span style="color: #000000;">&nbsp;url2.path<br />
<br />
</span><span style="color: #008000;">//</span><span style="color: #008000;">&nbsp;Implicit&nbsp;coersion&nbsp;by&nbsp;type&nbsp;of&nbsp;variable:</span><span style="color: #008000;"><br />
</span><span style="color: #000000;">URL&nbsp;url3&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;[</span><span style="color: #000000;">'</span><span style="color: #000000;">http</span><span style="color: #000000;">'</span><span style="color: #000000;">,&nbsp;</span><span style="color: #000000;">'</span><span style="color: #000000;">www.mrhaki.com</span><span style="color: #000000;">'</span><span style="color: #000000;">,&nbsp;</span><span style="color: #000000;">80</span><span style="color: #000000;">,&nbsp;</span><span style="color: #000000;">'</span><span style="color: #000000;">/</span><span style="color: #000000;">'</span><span style="color: #000000;">]&nbsp;<br />
</span><span style="color: #0000ff;">assert</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">'</span><span style="color: #000000;">http</span><span style="color: #000000;">'</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">==</span><span style="color: #000000;">&nbsp;url3.protocol<br />
</span><span style="color: #0000ff;">assert</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">'</span><span style="color: #000000;">www.mrhaki.com</span><span style="color: #000000;">'</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">==</span><span style="color: #000000;">&nbsp;url3.host<br />
</span><span style="color: #0000ff;">assert</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">80</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">==</span><span style="color: #000000;">&nbsp;url3.port<br />
</span><span style="color: #0000ff;">assert</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">'</span><span style="color: #000000;">/</span><span style="color: #000000;">'</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">==</span><span style="color: #000000;">&nbsp;url3.file<br />
</span><span style="color: #0000ff;">assert</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">'</span><span style="color: #000000;">/</span><span style="color: #000000;">'</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">==</span><span style="color: #000000;">&nbsp;url3.path&nbsp;&nbsp;&nbsp; <br />
</span></div>
<br />
<a href="http://mrhaki.blogspot.com/2009/09/groovy-goodness-using-lists-and-maps-as.html">http://mrhaki.blogspot.com/2009/09/groovy-goodness-using-lists-and-maps-as.html</a><br />
<br />
<img src ="http://www.blogjava.net/mstar/aggbug/294334.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/mstar/" target="_blank">黑灵</a> 2009-09-08 17:09 <a href="http://www.blogjava.net/mstar/archive/2009/09/08/294334.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[ZZ]Groovy Goodness: Using the Inject Method</title><link>http://www.blogjava.net/mstar/archive/2009/09/08/294331.html</link><dc:creator>黑灵</dc:creator><author>黑灵</author><pubDate>Tue, 08 Sep 2009 09:04:00 GMT</pubDate><guid>http://www.blogjava.net/mstar/archive/2009/09/08/294331.html</guid><wfw:comment>http://www.blogjava.net/mstar/comments/294331.html</wfw:comment><comments>http://www.blogjava.net/mstar/archive/2009/09/08/294331.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/mstar/comments/commentRss/294331.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/mstar/services/trackbacks/294331.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: Groovy has some features and methods we can categorize as functional programming. The inject() method is a so called higher-order function. Other languages call it a fold, reduce or accumulate. The inject() method processes a data structure with a closure and builds up a return value. The first parameter of the inject() method is the first value of the intermediary results of the second parameter: the closure. When we use the inject() we don't introduce any side effects, because we build up the &nbsp;&nbsp;<a href='http://www.blogjava.net/mstar/archive/2009/09/08/294331.html'>阅读全文</a><img src ="http://www.blogjava.net/mstar/aggbug/294331.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/mstar/" target="_blank">黑灵</a> 2009-09-08 17:04 <a href="http://www.blogjava.net/mstar/archive/2009/09/08/294331.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>制作图片缩略图的Groovy脚本</title><link>http://www.blogjava.net/mstar/archive/2009/08/25/292514.html</link><dc:creator>黑灵</dc:creator><author>黑灵</author><pubDate>Tue, 25 Aug 2009 05:49:00 GMT</pubDate><guid>http://www.blogjava.net/mstar/archive/2009/08/25/292514.html</guid><wfw:comment>http://www.blogjava.net/mstar/comments/292514.html</wfw:comment><comments>http://www.blogjava.net/mstar/archive/2009/08/25/292514.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/mstar/comments/commentRss/292514.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/mstar/services/trackbacks/292514.html</trackback:ping><description><![CDATA[<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #0000ff;">import</span><span style="color: #000000;">&nbsp;javax.imageio.ImageIO;<br />
</span><span style="color: #0000ff;">import</span><span style="color: #000000;">&nbsp;java.io.File;<br />
</span><span style="color: #0000ff;">import</span><span style="color: #000000;">&nbsp;java.io.IOException;<br />
</span><span style="color: #0000ff;">import</span><span style="color: #000000;">&nbsp;java.awt.image.BufferedImage;<br />
</span><span style="color: #0000ff;">import</span><span style="color: #000000;">&nbsp;java.awt.</span><span style="color: #000000;">*</span><span style="color: #000000;">;<br />
<br />
def&nbsp;createThumbnail(File&nbsp;input,&nbsp;File&nbsp;output,&nbsp;</span><span style="color: #0000ff;">int</span><span style="color: #000000;">&nbsp;length)&nbsp;</span><span style="color: #0000ff;">throws</span><span style="color: #000000;">&nbsp;IOException&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">if</span><span style="color: #000000;">&nbsp;(</span><span style="color: #000000;">!</span><span style="color: #000000;">input.exists())&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">throw</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;RuntimeException(</span><span style="color: #000000;">"</span><span style="color: #000000;">input&nbsp;file&nbsp;not&nbsp;exists!</span><span style="color: #000000;">"</span><span style="color: #000000;">);<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">if</span><span style="color: #000000;">&nbsp;(</span><span style="color: #000000;">!</span><span style="color: #000000;">output.exists())&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;output.createNewFile();<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;BufferedImage&nbsp;srcImage&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;ImageIO.read(input);<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">int</span><span style="color: #000000;">&nbsp;realWidth&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;srcImage.getWidth(</span><span style="color: #0000ff;">null</span><span style="color: #000000;">);<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">int</span><span style="color: #000000;">&nbsp;realHeight&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;srcImage.getHeight(</span><span style="color: #0000ff;">null</span><span style="color: #000000;">);<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">int</span><span style="color: #000000;">&nbsp;width,&nbsp;height;<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">if</span><span style="color: #000000;">&nbsp;(realWidth&nbsp;</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">&nbsp;length&nbsp;</span><span style="color: #000000;">&amp;&amp;</span><span style="color: #000000;">&nbsp;realHeight&nbsp;</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">&nbsp;length)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">&nbsp;保持原来大小</span><span style="color: #008000;"><br />
</span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;width&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;realWidth;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;height&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;realHeight;<br />
&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;</span><span style="color: #0000ff;">else</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">if</span><span style="color: #000000;">&nbsp;((length&nbsp;</span><span style="color: #000000;">*</span><span style="color: #000000;">&nbsp;realHeight)&nbsp;</span><span style="color: #000000;">/</span><span style="color: #000000;">&nbsp;realWidth&nbsp;</span><span style="color: #000000;">&gt;</span><span style="color: #000000;">&nbsp;length)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">&nbsp;按照实际高度来压缩<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">&nbsp;压缩后的宽</span><span style="color: #008000;"><br />
</span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;width&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;(length&nbsp;</span><span style="color: #000000;">*</span><span style="color: #000000;">&nbsp;realWidth)&nbsp;</span><span style="color: #000000;">/</span><span style="color: #000000;">&nbsp;realHeight;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">&nbsp;压缩后的高度</span><span style="color: #008000;"><br />
</span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;height&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;length;<br />
&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;</span><span style="color: #0000ff;">else</span><span style="color: #000000;">&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">&nbsp;按实际宽度来压缩<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">&nbsp;压缩后的宽</span><span style="color: #008000;"><br />
</span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;width&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;length;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">&nbsp;压缩后的高度</span><span style="color: #008000;"><br />
</span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;height&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;(length&nbsp;</span><span style="color: #000000;">*</span><span style="color: #000000;">&nbsp;realHeight)&nbsp;</span><span style="color: #000000;">/</span><span style="color: #000000;">&nbsp;realWidth;<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;Image&nbsp;newImage&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;srcImage.getScaledInstance(width,&nbsp;height,&nbsp;Image.SCALE_SMOOTH);<br />
&nbsp;&nbsp;&nbsp;&nbsp;BufferedImage&nbsp;targetImage&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;BufferedImage(width,&nbsp;height,&nbsp;BufferedImage.TYPE_INT_RGB);<br />
&nbsp;&nbsp;&nbsp;&nbsp;Graphics&nbsp;g&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;targetImage.getGraphics();<br />
&nbsp;&nbsp;&nbsp;&nbsp;g.drawImage(newImage,&nbsp;</span><span style="color: #000000;">0</span><span style="color: #000000;">,&nbsp;</span><span style="color: #000000;">0</span><span style="color: #000000;">,&nbsp;</span><span style="color: #0000ff;">null</span><span style="color: #000000;">);&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">&nbsp;绘制缩小后的图</span><span style="color: #008000;"><br />
</span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;g.dispose();<br />
&nbsp;&nbsp;&nbsp;&nbsp;ImageIO.write(targetImage,&nbsp;</span><span style="color: #000000;">"</span><span style="color: #000000;">jpeg</span><span style="color: #000000;">"</span><span style="color: #000000;">,&nbsp;output);<br />
}<br />
<br />
<br />
def&nbsp;dir&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;File(</span><span style="color: #000000;">"</span><span style="color: #000000;">d:/var/wormser/sample</span><span style="color: #000000;">"</span><span style="color: #000000;">)<br />
dir.eachFile{&nbsp;f</span><span style="color: #000000;">-&gt;</span><span style="color: #000000;"><br />
&nbsp;&nbsp;&nbsp;&nbsp;def&nbsp;name&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;f.name;<br />
&nbsp;&nbsp;&nbsp;&nbsp;println&nbsp;name<br />
&nbsp;&nbsp;&nbsp;&nbsp;newFileName&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;name.replaceAll(</span><span style="color: #000000;">/^</span><span style="color: #000000;">([a</span><span style="color: #000000;">-</span><span style="color: #000000;">zA</span><span style="color: #000000;">-</span><span style="color: #000000;">Z0</span><span style="color: #000000;">-</span><span style="color: #000000;">9_]</span><span style="color: #000000;">+</span><span style="color: #000000;">)\.([a</span><span style="color: #000000;">-</span><span style="color: #000000;">zA</span><span style="color: #000000;">-</span><span style="color: #000000;">Z0</span><span style="color: #000000;">-</span><span style="color: #000000;">9</span><span style="color: #000000;">]</span><span style="color: #000000;">+</span><span style="color: #000000;">)$</span><span style="color: #000000;">/</span><span style="color: #000000;">,&nbsp;</span><span style="color: #000000;">"</span><span style="color: #000000;">\$1_tb.jpg</span><span style="color: #000000;">"</span><span style="color: #000000;">)<br />
&nbsp;&nbsp;&nbsp;&nbsp;def&nbsp;newFile&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;File(dir.getAbsolutePath()&nbsp;</span><span style="color: #000000;">+</span><span style="color: #000000;">&nbsp;File.separator&nbsp;</span><span style="color: #000000;">+</span><span style="color: #000000;">&nbsp;newFileName)<br />
&nbsp;&nbsp;&nbsp;&nbsp;println&nbsp;newFileName<br />
&nbsp;&nbsp;&nbsp;&nbsp;createThumbnail(f,&nbsp;newFile,&nbsp;</span><span style="color: #000000;">160</span><span style="color: #000000;">)<br />
}</span></div>
<img src ="http://www.blogjava.net/mstar/aggbug/292514.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/mstar/" target="_blank">黑灵</a> 2009-08-25 13:49 <a href="http://www.blogjava.net/mstar/archive/2009/08/25/292514.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>改文件名的Groovy代码</title><link>http://www.blogjava.net/mstar/archive/2009/08/21/292062.html</link><dc:creator>黑灵</dc:creator><author>黑灵</author><pubDate>Fri, 21 Aug 2009 02:54:00 GMT</pubDate><guid>http://www.blogjava.net/mstar/archive/2009/08/21/292062.html</guid><wfw:comment>http://www.blogjava.net/mstar/comments/292062.html</wfw:comment><comments>http://www.blogjava.net/mstar/archive/2009/08/21/292062.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/mstar/comments/commentRss/292062.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/mstar/services/trackbacks/292062.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 改文件名的Groovy代码&nbsp;&nbsp;<a href='http://www.blogjava.net/mstar/archive/2009/08/21/292062.html'>阅读全文</a><img src ="http://www.blogjava.net/mstar/aggbug/292062.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/mstar/" target="_blank">黑灵</a> 2009-08-21 10:54 <a href="http://www.blogjava.net/mstar/archive/2009/08/21/292062.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>下划线分割字符串转换成驼峰式字符串</title><link>http://www.blogjava.net/mstar/archive/2009/06/22/283639.html</link><dc:creator>黑灵</dc:creator><author>黑灵</author><pubDate>Mon, 22 Jun 2009 11:19:00 GMT</pubDate><guid>http://www.blogjava.net/mstar/archive/2009/06/22/283639.html</guid><wfw:comment>http://www.blogjava.net/mstar/comments/283639.html</wfw:comment><comments>http://www.blogjava.net/mstar/archive/2009/06/22/283639.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.blogjava.net/mstar/comments/commentRss/283639.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/mstar/services/trackbacks/283639.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 下划线分割字符串转换成驼峰式字符串的groovy脚本&nbsp;&nbsp;<a href='http://www.blogjava.net/mstar/archive/2009/06/22/283639.html'>阅读全文</a><img src ="http://www.blogjava.net/mstar/aggbug/283639.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/mstar/" target="_blank">黑灵</a> 2009-06-22 19:19 <a href="http://www.blogjava.net/mstar/archive/2009/06/22/283639.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Grails develop tip 1</title><link>http://www.blogjava.net/mstar/archive/2009/05/31/279172.html</link><dc:creator>黑灵</dc:creator><author>黑灵</author><pubDate>Sun, 31 May 2009 02:44:00 GMT</pubDate><guid>http://www.blogjava.net/mstar/archive/2009/05/31/279172.html</guid><wfw:comment>http://www.blogjava.net/mstar/comments/279172.html</wfw:comment><comments>http://www.blogjava.net/mstar/archive/2009/05/31/279172.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/mstar/comments/commentRss/279172.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/mstar/services/trackbacks/279172.html</trackback:ping><description><![CDATA[<h1>
一些关于用Grails开发项目过程中的心得。</h1>
<h2>&nbsp;语言切换的链接：</h2>
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #0000ff;">&lt;</span><span style="color: #800000;">a&nbsp;</span><span style="color: #ff0000;">class</span><span style="color: #0000ff;">="flag"</span><span style="color: #ff0000;">&nbsp;href</span><span style="color: #0000ff;">="${createLink(action:actionName,&nbsp;controller:controllerName,&nbsp;params:&nbsp;params&nbsp;+&nbsp;[lang:'zh_CN'])}"</span><span style="color: #0000ff;">&gt;</span><span style="color: #000000;"><br />
</span><span style="color: #0000ff;">&lt;</span><span style="color: #800000;">img&nbsp;</span><span style="color: #ff0000;">src</span><span style="color: #0000ff;">="${createLinkTo(dir:'images/flag',file:'zh_CN_S.png')}"</span><span style="color: #ff0000;">&nbsp;alt</span><span style="color: #0000ff;">="中国語"</span><span style="color: #ff0000;">&nbsp;border</span><span style="color: #0000ff;">="0"</span><span style="color: #ff0000;">&nbsp;</span><span style="color: #0000ff;">/&gt;</span><span style="color: #000000;"><br />
</span><span style="color: #0000ff;">&lt;/</span><span style="color: #800000;">a</span><span style="color: #0000ff;">&gt;</span><span style="color: #000000;"><br />
</span><span style="color: #0000ff;">&lt;</span><span style="color: #800000;">a&nbsp;</span><span style="color: #ff0000;">class</span><span style="color: #0000ff;">="flag"</span><span style="color: #ff0000;">&nbsp;href</span><span style="color: #0000ff;">="${createLink(action:actionName,&nbsp;controller:controllerName,&nbsp;params:&nbsp;params&nbsp;+&nbsp;[lang:'ja'])}"</span><span style="color: #0000ff;">&gt;</span><span style="color: #000000;"><br />
</span><span style="color: #0000ff;">&lt;</span><span style="color: #800000;">img&nbsp;</span><span style="color: #ff0000;">src</span><span style="color: #0000ff;">="${createLinkTo(dir:'images/flag',file:'ja_JP_S.png')}"</span><span style="color: #ff0000;">&nbsp;alt</span><span style="color: #0000ff;">="日本語"</span><span style="color: #ff0000;">&nbsp;border</span><span style="color: #0000ff;">="0"</span><span style="color: #ff0000;">&nbsp;</span><span style="color: #0000ff;">/&gt;</span><span style="color: #000000;"><br />
</span><span style="color: #0000ff;">&lt;/</span><span style="color: #800000;">a</span><span style="color: #0000ff;">&gt;</span></div>
<br />
<img src ="http://www.blogjava.net/mstar/aggbug/279172.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/mstar/" target="_blank">黑灵</a> 2009-05-31 10:44 <a href="http://www.blogjava.net/mstar/archive/2009/05/31/279172.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>django useful tip</title><link>http://www.blogjava.net/mstar/archive/2008/07/11/Django_USEFUL_TIP.html</link><dc:creator>黑灵</dc:creator><author>黑灵</author><pubDate>Fri, 11 Jul 2008 04:07:00 GMT</pubDate><guid>http://www.blogjava.net/mstar/archive/2008/07/11/Django_USEFUL_TIP.html</guid><wfw:comment>http://www.blogjava.net/mstar/comments/214221.html</wfw:comment><comments>http://www.blogjava.net/mstar/archive/2008/07/11/Django_USEFUL_TIP.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.blogjava.net/mstar/comments/commentRss/214221.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/mstar/services/trackbacks/214221.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 最近在用django做一下项目, 我也是第一次使用django, 也是第一次比较正式的使用Python. 本文将记录一下使用django过程中遇到的一下问题和解决办法. 不断更新!&nbsp;&nbsp;<a href='http://www.blogjava.net/mstar/archive/2008/07/11/Django_USEFUL_TIP.html'>阅读全文</a><img src ="http://www.blogjava.net/mstar/aggbug/214221.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/mstar/" target="_blank">黑灵</a> 2008-07-11 12:07 <a href="http://www.blogjava.net/mstar/archive/2008/07/11/Django_USEFUL_TIP.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>如何在window系统中让py文件可以直接执行</title><link>http://www.blogjava.net/mstar/archive/2008/06/20/execute_py_file_in_console.html</link><dc:creator>黑灵</dc:creator><author>黑灵</author><pubDate>Fri, 20 Jun 2008 09:25:00 GMT</pubDate><guid>http://www.blogjava.net/mstar/archive/2008/06/20/execute_py_file_in_console.html</guid><wfw:comment>http://www.blogjava.net/mstar/comments/209506.html</wfw:comment><comments>http://www.blogjava.net/mstar/archive/2008/06/20/execute_py_file_in_console.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/mstar/comments/commentRss/209506.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/mstar/services/trackbacks/209506.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 正常情况下你如果是用安装文件安装的Python, 那么安装时会给你设置好这样的功能. 但是有时候你可能不小心改了配置, 或者不是用安装包安装的. 这是如果你在控制台直接输入:<br>  xxx.py arg1 arg2 这样的命令, 就不会显示"xxx.py"不是内部或外部命令，也不是可运行的程序或批处理文件。<br><br>比如你要用 django-admin.py startproject mysite 来建立项目.&nbsp;&nbsp;<a href='http://www.blogjava.net/mstar/archive/2008/06/20/execute_py_file_in_console.html'>阅读全文</a><img src ="http://www.blogjava.net/mstar/aggbug/209506.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/mstar/" target="_blank">黑灵</a> 2008-06-20 17:25 <a href="http://www.blogjava.net/mstar/archive/2008/06/20/execute_py_file_in_console.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>用Komodo调试Google App Engine应用程序</title><link>http://www.blogjava.net/mstar/archive/2008/06/12/Use_Komodo_Debug_GAE_APP.html</link><dc:creator>黑灵</dc:creator><author>黑灵</author><pubDate>Thu, 12 Jun 2008 08:30:00 GMT</pubDate><guid>http://www.blogjava.net/mstar/archive/2008/06/12/Use_Komodo_Debug_GAE_APP.html</guid><wfw:comment>http://www.blogjava.net/mstar/comments/207424.html</wfw:comment><comments>http://www.blogjava.net/mstar/archive/2008/06/12/Use_Komodo_Debug_GAE_APP.html#Feedback</comments><slash:comments>7</slash:comments><wfw:commentRss>http://www.blogjava.net/mstar/comments/commentRss/207424.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/mstar/services/trackbacks/207424.html</trackback:ping><description><![CDATA[Google App Engine是个好东西(简称GAE), 具体是什么我就不说了, GAE的应用是用Python来开发的. 具体请看这里:<br />http://code.google.com/appengine/<br /><br />Komodo是一个脚本语言IDE, 对Python支持的非常好, 现在个人版的也免费了. 用Komodo来调式Python程序也很方便. 但是GAE的运行方式和普通的python应用不太一样, 虽然原理是类似的, 但是不能直接在Komodo一点就运行调式.<br />关于Komodo:<br />http://www.activestate.com/Products/komodo_ide/index.mhtml<br /><br />这里要说的是如何用Komodo以调试的方式运行GAE的应用.<br /><br /><b>首先, 把你的GAE应用在Komodo中建立一个Project:</b><br /><br /><img src="http://www.blogjava.net/images/blogjava_net/mstar/pics2008/proj.png" alt="proj.png" width="211" border="0" height="328" /><br /><br /><b>然后在需要调式的代码里设置断点:</b><br /><br /><img src="http://www.blogjava.net/images/blogjava_net/mstar/pics2008/breakpoint.png" alt="breakpoint.png" width="430" border="0" height="300" /><br /><br /><b>然后点击工具栏上的调式运行按钮:</b><br /><br /><img src="http://www.blogjava.net/images/blogjava_net/mstar/pics2008/run.png" alt="run.png" width="238" border="0" height="128" /><br /><br /><b>会弹出对话框:</b><br /><br /><img src="http://www.blogjava.net/images/blogjava_net/mstar/pics2008/dlg.png" alt="dlg.png" width="562" border="0" height="556" /><br /><br /><b>点击new按钮, 可以设定一个名字.</b><br /><br />Script输入项, 要改为 D:\google\google_appengine\dev_appserver.py, 就是你的GAE安装目录下的dev_appserver.py文件. 其实如果你要在控制台上运行GAE也要运行这个文件, 不过是:<br />python dev_appserver.py 程序路径<br /><br />Script Arguments 输入程序的路径, 如果应用程序的路径在GAE目录下, 则写相对路径, 如果不是则写绝对路径. 其他的参数可以根据http://code.google.com/appengine/docs/thedevwebserver.html#Command_Line_Arguments来确定.<br /><br />Directory输入 GAE的路径<br /><br /><br /><b>切换到Envirement页:</b><br /><img src="http://www.blogjava.net/images/blogjava_net/mstar/pics2008/env.png" alt="env.png" width="559" border="0" height="550" /><br /><b><br />点击New..按钮, 创建一个PYTHONPATH变量</b><br /><img src="http://www.blogjava.net/images/blogjava_net/mstar/pics2008/new.png" alt="new.png" width="428" border="0" height="154" /><br /><br /><b>点击那个向下的箭头, 可以添加多个路径:</b><br /><img src="http://www.blogjava.net/images/blogjava_net/mstar/pics2008/var.png" alt="var.png" width="429" border="0" height="254" /><br /><br />第一项是%PYTHONPATH%, 代表原始的PYTHONPATH变量.<br />后面有4项, 是GAE中的的Python库路径<br />这里我预先在系统环境中设置了系统变量GAE_HOME是GAE的安装路径,GAE_LIB=%GAE_HOME%\lib<br />这四项是:<br />%GAE_HOME%<br />%GAE_LIB%\yaml<br />%GAE_LIB%\django<br />%GAE_LIB%\webob<br />设置完毕后点击OK按钮, 然后点击Debug Options菜单的OK按钮, 就开始运行应用程序了<br /><img src="http://www.blogjava.net/images/blogjava_net/mstar/pics2008/running.png" alt="running.png" width="617" border="0" height="488" /><br /><br />然后通过浏览器运行你的应用.<br /><br /><img src="http://www.blogjava.net/images/blogjava_net/mstar/pics2008/vars.png" alt="vars.png" width="567" border="0" height="583" /><br /><br />这样你就可以用Komodo开发调试你的GAE应用了, 很方便是吧!<br /><br /><img src ="http://www.blogjava.net/mstar/aggbug/207424.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/mstar/" target="_blank">黑灵</a> 2008-06-12 16:30 <a href="http://www.blogjava.net/mstar/archive/2008/06/12/Use_Komodo_Debug_GAE_APP.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Flex2 发现之旅：AS3中新的XML处理方法 - E4X</title><link>http://www.blogjava.net/mstar/archive/2007/05/31/121136.html</link><dc:creator>黑灵</dc:creator><author>黑灵</author><pubDate>Thu, 31 May 2007 05:28:00 GMT</pubDate><guid>http://www.blogjava.net/mstar/archive/2007/05/31/121136.html</guid><wfw:comment>http://www.blogjava.net/mstar/comments/121136.html</wfw:comment><comments>http://www.blogjava.net/mstar/archive/2007/05/31/121136.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/mstar/comments/commentRss/121136.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/mstar/services/trackbacks/121136.html</trackback:ping><description><![CDATA[
		<h2 class="post-title">Flex2 发现之旅：AS3中新的XML处理方法 - E4X</h2>
		<div class="tags">
                Tags:<a href="http://www.flexlib.cn/index.php/tag/actionscript" rel="tag">ActionScript</a> <a href="http://technorati.com/tag/ActionScript" rel="tag" class="technorati" target="_blank"><img src="http://www.flexlib.cn/images/icn-talkbubble.gif" alt="[T]" /></a>, <a href="http://www.flexlib.cn/index.php/tag/e4x" rel="tag">E4X</a> <a href="http://technorati.com/tag/E4X" rel="tag" class="technorati" target="_blank"><img src="http://www.flexlib.cn/images/icn-talkbubble.gif" alt="[T]" /></a>, <a href="http://www.flexlib.cn/index.php/tag/flash" rel="tag">Flash</a> <a href="http://technorati.com/tag/Flash" rel="tag" class="technorati" target="_blank"><img src="http://www.flexlib.cn/images/icn-talkbubble.gif" alt="[T]" /></a>, <a href="http://www.flexlib.cn/index.php/tag/flex" rel="tag">Flex</a> <a href="http://technorati.com/tag/Flex" rel="tag" class="technorati" target="_blank"><img src="http://www.flexlib.cn/images/icn-talkbubble.gif" alt="[T]" /></a>, <a href="http://www.flexlib.cn/index.php/tag/flex2" rel="tag">Flex2</a> <a href="http://technorati.com/tag/Flex2" rel="tag" class="technorati" target="_blank"><img src="http://www.flexlib.cn/images/icn-talkbubble.gif" alt="[T]" /></a></div>
		<div class="post-content">
				<p class="zh_p">今天我们来看看AS3中新的XML处理方法：<a href="http://www.ecma-international.org/publications/standards/Ecma-357.htm" hreflang="en" target="_blank">E4X</a>，
直到现在，ECMA脚本语言规范（ECMA-262）－－AscriptScript
3.0的核心基础，并没有提供任何的XML数据处理类或方法。AcriontScript之前的版本（从Flash
5中的ActionScript开始）有一些处理XML数据的类和方法，但是它们并不是基于ECMAScript标准的。</p>
				<p class="zh_p">新的ECMA脚本语言规范第4版草稿中定义了一系列新的处理XML数据的类和方法。这些类和方法的集合并命名为E4X（"ECMAScript for XML"），ActionScript 3.0包括如下新的E4X类： <a href="http://livedocs.macromedia.com/labs/1/flex/langref/XML.html" target="_blank">XML</a>、<a href="http://livedocs.macromedia.com/labs/1/flex/langref/XMLList.html" target="_blank">XMLList</a>、 <a href="http://livedocs.macromedia.com/labs/1/flex/langref/QName.html" target="_blank">QName</a>和<a href="http://livedocs.macromedia.com/labs/1/flex/langref/Namespace.html" target="_blank">Namespace</a>。</p>
				<p class="zh_p">E4X类的方法、属性和操作的开法基于以下的目标：</p>
				<ul>
						<li>简单－－E4X尽可能的使得处理XML数据的代码容易编写并且易于理解。</li>
						<li>一致性－－E4X的方法于Actionscript的其它部分协调一致。</li>
						<li>友好－－实用非常好理解的操作符处理XML数据，如点号（.）。</li>
				</ul>
				<p class="zh_p">注意：为避免与E4X中的新的XML类冲突，原来ActionScript 2.0中的XML类在ActionScript 3.0被重命名为<a href="http://livedocs.macromedia.com/labs/1/flex/langref/flash/xml/XMLDocument.html" target="_blank">XMLDocument</a>，为了向前兼容，在ActionScript 3.0中遗留下来的类－－XML、XMLNode、XMLParser和XMLTag--被包含进了<a href="http://livedocs.macromedia.com/labs/1/flex/langref/flash/xml/package-detail.html" target="_blank">flash.xml</a>包中。新的E4X类是核心类－－使用它们不需要import任何包。</p>
				<p class="zh_p">
						<strong>初始化XML对象</strong>
				</p>
				<p class="zh_p">XML对象可以代表一个XML元素、属性、注释、处理指令或文本元素。在ActionScript 3.0中我们可以直接将XML数据赋值给变量：</p>
				<p>
				</p>
				<pre>var myXML:XML =<br />   &lt;order&gt;<br />       &lt;item id='1'&gt;<br />           &lt;menuName&gt;burger&lt;/menuName&gt;<br />           &lt;price&gt;3.95&lt;/price&gt;<br />       &lt;/item&gt;<br />       &lt;item id='2'&gt;<br />           &lt;menuName&gt;fries&lt;/menuName&gt;<br />           &lt;price&gt;1.45&lt;/price&gt;<br />       &lt;/item&gt;<br />   &lt;/order&gt;			</pre>
				<p class="zh_p">你也可以使用new 构造器来从XML数据文本创建一个XML对象实例：</p>
				<p>
				</p>
				<pre>var myXML:XML = new XML("&lt;order&gt;&lt;item id='1'&gt;&lt;menuName&gt;burger&lt;/menuName&gt;&lt;price&gt;3.95&lt;/price&gt;&lt;/item&gt;&lt;/order&gt;")<br /></pre>
				<p class="zh_p">如果XML数据不是格式完好的（如少了结束标签），那么将会出现运行时错误。</p>
				<p class="zh_p">注意，你也可以将变量实例传入XML数据中：</p>
				<pre>var tagname:String = "item";<br />var attributename:String = "id";<br />var attributevalue:String = 5;<br />var content:String = "Chicken";<br />var x:XML = &lt;{tagname} {attributename}={attributevalue}&gt;{content}&lt;/{tagname}&gt;;<br />trace (x.toXMLString())<br />   // Output: &lt;item id="5"&gt;Chicken&lt;/item&gt;</pre>
				<p class="zh_p">通常，我们的应用是从外部源导入XML数据，如web service或RSS feed,以下是一个从远程URL导入XML数据的例子：</p>
				<p>
				</p>
				<pre>var myXML:XML = new XML();<br />var XML_URL:String = "http://www.example.com/Sample3.xml";<br />//创建URLRequest。<br />var myXMLURL:URLRequest = new URLRequest(XML_URL);<br />//使用URLLoader导入数据。<br />var myLoader:URLLoader = new URLLoader(myXMLURL);<br />//添加事件监听器，以在XML数据导入完成后处理XML数据。<br />myLoader.addEventListener("complete", xmlLoaded);<br />//导入完成后，创建使用导入的数据创建XML对象<br />function xmlLoaded(evtObj:Event) {<br />       var myXML:XML = XML(myLoader.data);<br />       trace("Data loaded.");<br /><br />}<br /></pre>
				<p class="zh_p">为了演示代码的清晰性，本文中的大部份示例都第1种直接使用文本的方法创建XML对象。</p>
				<p class="zh_p">E4X包含一些直观的方法XML数据的操作符（如.和@:用于访问属性）：</p>
				<p>
				</p>
				<pre>//获取第1个item的menuName值<br />trace(myXML.item[0].menuName); // Output: burger<br />//获取第1个item的id属性值<br />trace(myXML.item[0].@id);//Output:1<br />//获取id属性为2的item的menuName值<br />trace(myXML.item.(@id==2).menuName); // Output: fries<br />//获取menuName为burger的item的price值<br />trace(myXML.item.(menuName=="burger").price); // Output: 3.95<br /></pre>
				<p class="zh_p">你也可以使用appendChild()方法给XML添加新的子节点：</p>
				<p>
				</p>
				<pre>var newItem:XML =<br />   &lt;item id="3"&gt;<br />       &lt;menuName&gt;medium cola&lt;/menuName&gt;<br />       &lt;price&gt;1.25&lt;/price&gt;<br />   &lt;/item&gt;<br /><br />myXML.appendChild(newItem);<br /></pre>
				<p class="zh_p">当然你也可以使用@和.操作符来更新数据:</p>
				<p>
				</p>
				<pre>myXML.item[0].menuName="regular burger";<br />myXML.item[1].menuName="small fries";<br />myXML.item[2].menuName="medium cola";<br /><br />myXML.item.(menuName=="regular burger").@quantity = "2";<br />myXML.item.(menuName=="small fries").@quantity = "2";<br />myXML.item.(menuName=="medium cola").@quantity = "2";<br /></pre>
				<p class="zh_p">
						<strong>访问XML数据</strong>
				</p>
				<p class="zh_p">你可以使用.（点号）和..操作符访问XML对象的子节点，使用@操作符访问某一节点的属性。考虑以下XML对象：
</p>
				<pre>
						<br />var x:XML =<br />      &lt;book ISBN="0942407296"&gt;<br />           &lt;title&gt;Baking Extravagant Pastries with Kumquats&lt;/title&gt;<br />           &lt;author&gt;<br />               &lt;lastName&gt;Contino&lt;/lastName&gt;<br />               &lt;firstName&gt;Chuck&lt;/firstName&gt;<br />           &lt;/author&gt;<br />           &lt;pageCount&gt;238&lt;/pageCount&gt;<br />       &lt;/book&gt;<br />       &lt;book ISBN="0865436401"&gt;<br />           &lt;title&gt;Emu Care and Breeding&lt;/title&gt;<br />           &lt;editor&gt;<br />               &lt;lastName&gt;Case&lt;/lastName&gt;<br />               &lt;firstName&gt;Justin&lt;/firstName&gt;<br />           &lt;/editor&gt;<br />           &lt;pageCount&gt;115&lt;/pageCount&gt;<br />       &lt;/book&gt;<br />   &lt;/order&gt;<br /></pre>
				<p class="zh_p">对象x.book表示一个包含所有名称为book的子节点的XMLList对象，该XMLList包含两个XML对象（两个book节点）。</p>
				<p class="zh_p">对象x..lastName表示一个包含XML树结构下部所有的lastName属性的XMLList对象，该XMList包含两个XML对象（两个LastName属性）。</p>
				<p class="zh_p">对象x.book.editor.lastName表示一个包含所有x对象的所有名称为book的子节点的所有名称为
editor的子节点的所有lastName节点的XMLList对象，该XMLList只包含一个XML对象(值为"Case"的lastName属
性)。</p>
				<p class="zh_p">
						<strong>访问父节点和子节点</strong>
				</p>
				<p class="zh_p">parent()方法返回XML对象的父节点。</p>
				<p class="zh_p">你可以使用子节点列表的顺序索引值来访问特定的子节点，例如，假定某一XML对象x有两个名称为book的子节点，你可以如下访问它们：</p>
				<pre>//第1个book节点<br />x.book[0]<br />//第2个book节点<br />x.book[1]<br /></pre>
				<p class="zh_p">要访问孙子节点，我们可以如下直接使用儿子和孙子节点的索引值来访问：</p>
				<pre>x.book[0].title[0]</pre>
				<p class="zh_p">不过如果x.book[0]只有一个名称为title的子节点的话，那么可以省略索引：</p>
				<pre>x.book[0].title</pre>
				<p class="zh_p">类似的，如果x对象只有一个book子节点，并且该book节点的子节点对象只有一个title对象，那么两个索引值都可以省略：</p>
				<p class="zh_p">x.book.title</p>
				<p class="zh_p">注意，你也可以使用child()方法，直接使用名称访问特定的子节点：</p>
				<pre>var x.XML =<br />       &lt;order&gt;<br />           &lt;book&gt;<br />               &lt;title&gt;Dictionary&lt;/title&gt;<br />           &lt;/book&gt;<br />       &lt;/order&gt;<br /><br />var childName:String = "book";<br />trace (x.child(childName).title) // Output: Dictionary<br /></pre>
				<p class="zh_p">
						<strong>访问属性</strong>
				</p>
				<p class="zh_p">我们使用使用@操作符访问XMLNode属性：</p>
				<pre>var myXML:XML =<br />   &lt;order&gt;<br />       &lt;item id='1'&gt;<br />           &lt;menuName&gt;burger&lt;/menuName&gt;<br />           &lt;price&gt;3.95&lt;/price&gt;<br />       &lt;/item&gt;<br />       &lt;item id='2'&gt;<br />           &lt;menuName&gt;fries&lt;/menuName&gt;<br />           &lt;price&gt;1.45&lt;/price&gt;<br />       &lt;/item&gt;<br />   &lt;/order&gt;<br /><br />//获取第1个item的id属性值<br />trace(myXML.item[0].@id);//Output:1<br /></pre>
				<p class="zh_p">
						<strong>使用属性或元素值过滤XML数据</strong>
				</p>
				<p class="zh_p">我们可以使用特定的元素名称和属性值来定位到特定的元素考虑以下XML对象：</p>
				<pre>var x:XML =<br />   &lt;employeeList&gt;<br />       &lt;employee id="347"&gt;<br />           &lt;lastName&gt;Zmed&lt;/lastName&gt;<br />           &lt;firstName&gt;Sue&lt;/firstName&gt;<br />           &lt;position&gt;Data analyst&lt;/position&gt;<br />       &lt;/employee&gt;<br />       &lt;employee id="348"&gt;<br />           &lt;lastName&gt;McGee&lt;/lastName&gt;<br />           &lt;firstName&gt;Chuck&lt;/firstName&gt;<br />           &lt;position&gt;Jr. data analyst&lt;/position&gt;<br />       &lt;/employee&gt;<br />   &lt;/employeeList&gt;<br /><br /></pre>
				<p class="zh_p">以下是正确的访问方法：</p>
				<pre>//lastName为“McGee”的employee对象，第1个employee节点<br />x.employee.(lastName == "McGee") // The first employee node<br />//lastName为“McGee”的employee对象的firstName节点，第1个employee节点的节点<br />x.employee.(lastName == "McGee").firstName // The firstName property of that node<br />//lastName为“McGee”的id属性<br />x.employee.(lastName == "McGee").@id // The value of the id attribute<br />//所有id属性值为347的employee对象列表<br />x.employee.(@id == 347)<br />//id属性值为347的employee对象的lastName子节点<br />x.employee.(@id == 347).lastName<br />//所有id属性值大于347的employee对象列表<br />x.employee.(@id &gt; 300) // An XML list with both employee properties<br />//所有position子节点值包含“analyst”的employee对象列表<br />x.employee.(position.toString().search("analyst") &gt; -1)<br /></pre>
				<p class="zh_p">
						<strong>使用for ... in和for each ... in 语句</strong>
				</p>
				<p class="zh_p">ActionScript 3.0 包括了一个新用来遍历XMLList对象的的for ... in语句和for
each ... in语句。例如，考虑以下XML对象，myXML和myXML..item XMLList对象（包含两个item
XML对象节点的XML列表）：</p>
				<pre>var myXML:XML =<br />   &lt;order&gt;<br />       &lt;item id='1' quantity='2'&gt;<br />           &lt;menuName&gt;burger&lt;/menuName&gt;<br />           &lt;price&gt;3.95&lt;/price&gt;<br />       &lt;/item&gt;<br />       &lt;item id='2' quantity='2'&gt;<br />           &lt;menuName&gt;fries&lt;/menuName&gt;<br />           &lt;price&gt;1.45&lt;/price&gt;<br />       &lt;/item&gt;<br />   &lt;/order&gt;<br /></pre>
				<p class="zh_p">for ... in语句可以让我们遍历XMLList的所有属性名称,实际上就是个节点的索引值：</p>
				<pre>var total:Number = 0;<br />for (var pname:String in myXML..item)<br />{<br />    total += Number(myXML.item.@quantity[pname]) * Number(myXML.item.price[pname]);<br />}<br /></pre>
				<p class="zh_p">for each ... in语句遍历XMLList的所有节点：</p>
				<pre>var total2:Number = 0;<br />for each (var item:XML in myXML..item)<br />{<br />  total2 += Number(item@quantity) * Number(item.price);<br />}<br /></pre>
				<p class="zh_p">
						<strong>使用with语句</strong>
				</p>
				<p class="zh_p">我们可以使用with语句，来指明后续的属性和节点值都是基于某一XML对象，前面的for each ... in示例代码，使用with语句的代码如下：</p>
				<pre>var total:Number = 0;<br />for each (var item in myXML..item)<br />{<br />   with (item)<br />    {<br />         //{内的属性和节点对象都是基于item XML对象的，所有不需要使用item.来访问。<br />         total += Number(@quantity) * Number(price);<br />     }<br /><br />}<br />trace(total);<br /></pre>
				<p class="zh_p">
						<strong>修改XML对象</strong>
				</p>
				<p class="zh_p">我们可以使用prependChild()方法或者appendChild()方法在XML对象的子节点列表的前面或者最后面添加节点：</p>
				<pre>var x1:XML = &lt;p&gt;Paragraph 1&lt;/p&gt;<br />var x2:XML = &lt;p&gt;Paragraph 2&lt;/p&gt;<br />var x:XML = &lt;body&gt;&lt;/body&gt;<br />x = x.appendChild(x1);<br />x = x.appendChild(x2);<br />x = x.prependChild(&lt;p&gt;Paragraph 0&lt;/p&gt;);<br /><br />// x == &lt;body&gt;&lt;p&gt;Paragraph 0&lt;/p&gt;&lt;p&gt;Paragraph 1&lt;/p&gt;&lt;p&gt;Paragraph 2&lt;/p&gt;&lt;/body&gt;<br /></pre>
				<p class="zh_p">使用insertChildBefore()方法或者insertChildAfter()方法在特定的节点前面活着回后面添加节点：</p>
				<pre>var x:XML =<br />   &lt;body&gt;<br />       &lt;p&gt;Paragraph 1&lt;/p&gt; <br />       &lt;p&gt;Paragraph 2&lt;/p&gt;<br />   &lt;/body&gt;<br /><br />var newNode:XML = &lt;p&gt;Paragraph 1.5&lt;/p&gt;<br />x = x.insertChildAfter(x.p[0], newNode)<br />x = x.insertChildBefore(x.p[2], &lt;p&gt;Paragraph 1.75&lt;/p&gt;)<br /></pre>
				<p class="zh_p">注意，我们也可以在构造XML对象的时候使用大括号（{和}）来引用变量：</p>
				<pre>var ids:Array = [121, 122, 123];<br />var names:Array = [["Murphy","Pat"],["Thibaut","Jean"], ["Smith","Vijay"]]<br />var x:XML = new XML("&lt;employeeList&gt;&lt;/employeeList&gt;");<br /><br />for (var i:int = 0; i &lt; 3; i++) {<br />          var newnode:XML = new XML(); <br />           newnode =<br />               &lt;employee id={ids[i]}&gt;<br />                   &lt;last&gt;{names[i][0]}&lt;/last&gt;<br />                   &lt;first&gt;{names[i][1]}&lt;/first&gt;<br />               &lt;/employee&gt;<br /><br />           x = x.appendChild(newnode)<br /><br />}<br /></pre>
				<p class="zh_p">我们也可以使用=操作符来给XML对象节点赋值：</p>
				<pre>var x:XML =<br />   &lt;employee&gt;<br />       &lt;lastname&gt;Smith&lt;/lastname&gt;<br />   &lt;/employee&gt;<br /><br />x.firstname = "Jean";<br />x.@id = "239";<br /></pre>
				<p class="zh_p">以上代码将把XML对象X设置成如下：</p>
				<pre>&lt;employee id="239"&gt;<br />   &lt;lastname&gt;Smith&lt;/lastname&gt;<br />   &lt;firstname&gt;Jean&lt;/firstname&gt;<br />   &lt;/employee&gt;<br /></pre>
				<p class="zh_p">我们也可以使用+和+=操作符来连结XMLList：</p>
				<pre>var x1:XML = &lt;a&gt;test1&lt;/a&gt;<br />var x2:XML = &lt;b&gt;test2&lt;/b&gt;<br />var xList:XMLList = x1 + x2;<br />xList += &lt;c&gt;test3&lt;/c&gt;<br /></pre>
				<p class="zh_p">
						<strong>
								<em>删除XML对象</em>
						</strong>
				</p>
				<p class="zh_p">E4X规范中定义有delete和deleteByIndex方法用来删除特定的XML节点，但是在当前版本的
ActionScript
3.0实现中，并没有实现这两个方法，所有我们不能直接使用这两个方法，不过我们可以使用遍历XML树，剔除特定的节点，重新构造新的XML对象的方法来
删除某一特定节点，以下我们将删除employee的lastname子节点：</p>
				<pre>private function deleteByIndex(xmlNode:XML,index:Number):XML{<br />    var newStr:String='';<br />    newStr+='&lt;'+xmlNode.localName();<br />    for each (var att:XML in xmlNode.attributes()){<br />        newStr+=' '+att.localName()+'="'+att.toString()+'"';<br />    }<br />    newStr+='&gt;';<br />    var i:Number=0;<br />    for each(var node:XML in xmlNode.children()){<br />        if(i!=index)<br />            newStr+=node.toXMLString();<br />        i++;<br />    }<br />    newStr+='&lt;/'+xmlNode.localName()+'/&gt;';<br />    return new XML(newStr);<br />}<br /><br />var myXML:XML=<br />   &lt;employee id="239"&gt;<br />           &lt;lastname&gt;Smith&lt;/lastname&gt;<br />           &lt;firstname&gt;Jean&lt;/firstname&gt;<br />           &lt;address&gt;<br />           	&lt;city&gt;shangrao&lt;/city&gt;<br />           	&lt;load&gt;daihu&lt;/load&gt;<br />           	&lt;no&gt;5&lt;/no&gt;<br />           &lt;/address&gt;<br />       &lt;/employee&gt;<br /><br />myXML=deleteByIndex(myXML,0);<br /></pre>
				<p class="zh_p">以上的deleteByIndex函数有两个参数，第1的参数是被删除节点的父节点，第2的参数是被删除节点的在父节点的子节点列表中索引值。先遍历父节点的索引属性，然后遍历其所有的子节点，跳过我们要删除的节点，然后将它们组合成新的XML对象返回。</p>
				<p class="zh_p">如果XML对象非常复杂，数据量必较大的话，以上删除节点的实现方法效率是非常好低，所有正确的选择还是使用E4X定义的删除方法，不过这个功能要等到ActionScript 3.0的下一个测试版本才能够实现。</p>
				<p class="zh_p">
						<strong>XML类型转换</strong>
				</p>
				<p class="zh_p">我们可以将XML对象和XMLList对象转换为字符串，同样的，我们也可以将字符串转换为XML对象和XMLList对象。顺便，请记住所有的XML属性值、名称和文本值都是字符串。</p>
				<p class="zh_p">
						<strong>转换XML和XMLList对象为字符串</strong>
				</p>
				<p class="zh_p">XML对象和XMLList对象都有有两个成员方法：toString()和toXMLString()方法。
toXMLString()方法返回包含所有标签、属性、命名空间声明和XML对象内容的字符串，对与复杂的XML对象（包含有子元素），
toString()方法的效果和toXMLString()方法的效果一样，但是对与简单的XML对象（只包含一个文本元素），toString()方
法只返回元素的文本内容：</p>
				<pre>var myXML:XML =<br />  &lt;order&gt;<br />       &lt;item id='1' quantity='2'&gt;<br />           &lt;menuName&gt;burger&lt;/menuName&gt;<br />           &lt;price&gt;3.95&lt;/price&gt;<br />       &lt;/item&gt;<br />   &lt;order&gt;<br /><br />trace(myXML.item<a href="http://www.flexlib.cn/index.php/2006/01/04/0">0</a>.menuName.toXMLString())<br />    // Output: &lt;menuName&gt;burger&lt;/menuName&gt;<br />trace(myXML.item<a href="http://www.flexlib.cn/index.php/2006/01/04/0">0</a>.menuName.toString())<br />    // Output: burger<br /></pre>
				<p class="zh_p">将文本字符串转换为XML对象</p>
				<p class="zh_p">我们可以使用new构造方法从字符串创建一个XML对象</p>
				<pre>var x:XML = new XML('&lt;a&gt;test&lt;b&gt;');</pre>
				<p class="zh_p">但是如果我们试图将一个非XML或者结构不完整的字符串转换为XML对象，那么将会报运行时错误：</p>
				<pre>var x:XML = new XML('&lt;a&gt;test'); // Throws an error</pre>
				<p class="zh_p">
						<strong>从Internet读取Rss Fead数据</strong>
				</p>
				<p class="zh_p">以下代码将读取本站的rss fead数据：</p>
				<pre>&lt;?xml version="1.0" encoding="utf-8"?&gt;<br />  &lt;mx:Application xmlns:mx="http://www.macromedia.com/2005/mxml" xmlns="*" creationComplete="doInit()"&gt;<br />  &lt;mx:Script&gt;<br />  &lt;![CDATA[<br />    private function doInit():Void{<br />      getRssData("http://blog.eshangrao.com/rss.php",ta_view);<br />    }<br /><br />    public function getRssData(url:String, outTxt:TextArea):Void<br />    {<br />      private var myXMLURL:URLRequest = new URLRequest(url);<br />      private var myLoader:URLLoader = new URLLoader(myXMLURL);<br />      myLoader.addEventListener("complete", xmlLoaded);<br />    }<br />    private function xmlLoaded(event:Event):Void{<br />      ta_view.text='load ok';<br />      var myLoader:URLLoader = URLLoader(event.target);<br />      XML.ignoreProcessingInstructions=false;<br />      var myXML:XML =new XML(myLoader.data);<br />      private var outStr:String = "";<br />      for each (var item:XML in myXML.children()) {<br />        if(item.localName()=='item'){<br />          outStr += "&lt;p&gt;&lt;b&gt;" + item.children()<a href="http://www.flexlib.cn/index.php/2006/01/04/0">0</a>.toString() + ":&lt;/b&gt;&lt;/p&gt;&lt;p&gt;";<br />          outStr += item.children()<a href="http://www.flexlib.cn/index.php/2006/01/04/6">6</a>.toString() ;<br />          outStr += " &lt;br/&gt;&lt;a href='" + item.children()<a href="http://www.flexlib.cn/index.php/2006/01/04/1">1</a>.toString();<br />          outStr += "'&gt;&lt;font color='#008000'&gt;More...&lt;/font&gt;&lt;/a&gt;&lt;/p&gt;";<br />        }<br />      }<br />      //ta_view.text=myXML.toString();<br />      ta_view.htmlText = outStr;<br />    }<br />  ]]&gt;<br />  &lt;/mx:Script&gt;<br />  &lt;mx:Canvas width="100%" height="100%"&gt;<br />    &lt;mx:TextArea id="ta_view"&gt;<br />      &lt;mx:layoutConstraints&gt;<br />        &lt;mx:EdgeAnchor bottom="10" top="10" left="10" right="10"/&gt;<br />      &lt;/mx:layoutConstraints&gt;<br />    &lt;/mx:TextArea&gt;<br />  &lt;/mx:Canvas&gt;<br />&lt;/mx:Application&gt;<br /></pre>
				<p class="zh_p">
						<a href="http://labs.eshangrao.com/flex/2/RssReaderExample.html" target="_blank">运行示例</a>(FlashPlayer8.5 required)</p>
				<p class="zh_p">注意，我们没有直接使用节点名称访问节点（不知道为什么，如果使用item.title访问title节点的话，返回是
空的，可能跟我的RSS
XML的中rdf指令有关，如果有朋友知道解决的办法，请告诉我），而是使用了children()方法，该方法返回某一XML对象的所有子节点对象，</p>
				<p class="zh_p">获取更多AS3中E4X类的使用信息，请查看<a href="http://livedocs.macromedia.com/labs/1/flex/00002041.html" hreflang="en">Adobe在线文档</a>，详细API参考：<a href="http://livedocs.macromedia.com/labs/1/flex/langref/index.html">Adobe Flex 2 API文档</a></p>
		</div>
<img src ="http://www.blogjava.net/mstar/aggbug/121136.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/mstar/" target="_blank">黑灵</a> 2007-05-31 13:28 <a href="http://www.blogjava.net/mstar/archive/2007/05/31/121136.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[备份]动态更改页面的CSS</title><link>http://www.blogjava.net/mstar/archive/2006/06/14/52686.html</link><dc:creator>黑灵</dc:creator><author>黑灵</author><pubDate>Wed, 14 Jun 2006 03:45:00 GMT</pubDate><guid>http://www.blogjava.net/mstar/archive/2006/06/14/52686.html</guid><wfw:comment>http://www.blogjava.net/mstar/comments/52686.html</wfw:comment><comments>http://www.blogjava.net/mstar/archive/2006/06/14/52686.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/mstar/comments/commentRss/52686.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/mstar/services/trackbacks/52686.html</trackback:ping><description><![CDATA[
		<h1 class="title">
				<a href="http://www.alistapart.com/articles/alternate">Alternative Style: Working With Alternate Style Sheets</a>
		</h1>
		<div id="articletext">
				<p>
						<br />
						<br />So you've got a web page. You’ve marked it up with structural XHTML. You’ve also been a good little web developer and used style sheets to control what your document looks like. You’ve even gone the extra mile and created several alternative style sheets to show how hardcore you are.</p>
				<p>Great. But now you need a cross-browser way to dynamically switch between the style sheets.</p>
				<h2>Styling your site</h2>
				<p>Style sheets can be associated with documents using a list of link elements in the head. There are three different relationships external style sheets can have with the document: persistent, preferred, and alternate.</p>
				<h3>Persistent</h3>
				<p>These style sheets are always enabled (they are always “on”) and are combined with the active style sheet. They can be used for shared rules common to every style sheet. To make a style sheet persistent, the rel attribute is set to “stylesheet” and no title attribute is set.</p>
				<p>To make the style sheet <code>paul.css</code> persistent, the following link element would be included in the head:</p>
				<pre>&lt;link rel="stylesheet" 
type="text/css" href="paul.css" /&gt;
</pre>
				<h3>Preferred</h3>
				<p>These style sheets are enabled by default (they are “on” when the page is loaded). They can then be disabled if the user selects an alternate style sheet.</p>
				<p>To make a style sheet preferred, the <code>rel</code> attribute is set to “stylesheet” and the style sheet is named with the <code>title</code> attribute. </p>
				<p>Several preferred style sheets can be grouped together by giving them identical title attributes. These grouped style sheets are then all enabled and disabled together. If more than one group of preferred style sheets are declared, the first group takes precedence.</p>
				<p>To make <code>paul.css</code> preferred, a title attribute is added, giving the default style a name.</p>
				<pre>&lt;link rel="stylesheet" 
type="text/css" href="paul.css" 
title="bog standard" /&gt;
</pre>
				<h3>Alternate</h3>
				<p>These style sheets can be selected by the visitor as alternatives to the preferred style sheet. This allows the visitor to personalize a site and choose his or her favorite scheme. They can also be used for accessibility.</p>
				<p>To specify an alternate style sheet, the rel attribute is set to “alternate stylesheet” and the style sheet is named with a title attribute. As with preferred sheets, these style sheets can also be grouped together by giving them identical title attributes.</p>
				<p>Using the previous example again; to make <code>paul.css</code> into an alternate style sheet, the keyword “alternate” is added to the rel attribute.</p>
				<pre>&lt;link rel="alternate stylesheet" 
type="text/css" href="paul.css"
title="wacky" /&gt;
</pre>
				<p>Note that these relationships only apply to external style sheets which are included using the link element.</p>
				<h2>Swappin’ Styles</h2>
				<p>When a document is initially loaded, the persistent and preferred style sheets are applied to the document. The alternate style sheets can then be selected by the user. The W3C tells us that the browser should give us a choice of the style sheet we want to use, and suggests that perhaps a drop–down menu or tool bar will be provided.</p>
				<p>So far, so good. We have several style sheets and the visitor can choose their favorite from a menu. But then we encounter a problem. A major one. Mozilla provides a menu to select the style sheet we want to use under the view menu item. But Microsoft Internet Explorer (MSIE) provides no such menu. So we have several style sheets, and no way to access them in MSIE.</p>
				<p>Here’s where a little bit of JavaScript can be used along with the DOM to provide a way for MSIE and Mozilla users to select the style sheet they want to use. Their preference can also be stored in a cookie. And because we are using the link tags as the W3C tells us to, the JavaScript doesn’t interfere with the menu in Mozilla, and it degrades very gracefully.</p>
				<h2>The Script</h2>
				<p>First we need the script to be able to differentiate between the three different types of style sheet. This is relatively easy to do, as we only need to check two of the attributes of each link element.</p>
				<p>Is it a link to a style sheet?</p>
				<pre>HTMLLinkElement.getAttribute("rel").indexOf("style") != -1
</pre>
				<p>Is there a title attribute?</p>
				<pre>HTMLListElement.getAttribute("title")
</pre>
				<p>Does the rel attribute contain the keyword "alternate"?</p>
				<pre>HTMLLinkElement.getAttribute("rel").indexOf("alt") != -1
</pre>
				<p>Note that we check for the string “alt” because some browsers accept the keyword “alternative” in place of “alternate.”</p>
				<p>Using these three checks we can write a function to switch style sheets. This involves looping through every link element in the document, disabling all preferred and alternate style sheets that we <em>don’t</em> want active, and enabling all preferred and alternate style sheets that we <em>do</em> want active.</p>
				<p>Note that only preferred and alternate style sheet link elements will have a title attribute.</p>
				<p>The change function looks like this:</p>
				<pre> 
function setActiveStyleSheet(title) {
   var i, a, main;
   for(i=0; (a = document.getElementsByTagName("link")[i]); i++) {
     if(a.getAttribute("rel").indexOf("style") != -1
        &amp;&amp; a.getAttribute("title")) {
       a.disabled = true;
       if(a.getAttribute("title") == title) a.disabled = false;
     }
   }
}
</pre>
				<h3>Cookies</h3>
				<p>Now we can change the style sheet. Cool. We have a more personalized page. Excellent. But we don’t have a personalized site. The preference is only applied to the current page; when we leave the current page the preference leaves with us. This situation, however, can be rectified with a cookie.</p>
				<p>To store a cookie we need another function to return the current style sheet. We also need two functions to store and read the cookie.</p>
				<p>To return the current style sheet we look for an active preferred or alternate style sheet and check its title.</p>
				<p>First we loop through all the link elements in the document again. We then check whether the link is a style sheet. If it is, we check whether the style sheet has a title. This tells us that the style sheet is either preferred or alternative.</p>
				<p>The last check is to see whether or not the style sheet is active. If all three checks return true, we have the current style sheet and we can return the title.</p>
				<p>The function ends up looking like this:</p>
				<pre> 
function getActiveStyleSheet() {
var i, a;
 for(i=0; (a = document.getElementsByTagName("link")[i]); i++) {
  if(a.getAttribute("rel").indexOf("style") != -1
  &amp;&amp; a.getAttribute("title")
  &amp;&amp; !a.disabled) return a.getAttribute("title");
  }
  return null;
}
</pre>
				<p>As this is an article on style, and cookies are a completely different topic, I won’t explain the cookie functions here, but I will include them for your convenience (these <a href="http://www.xs4all.nl/%7Eppk/js/cookies.html">functions</a> are written by ALA author <a href="http://www.xs4all.nl/%7Eppk/js/index.html?/%7Eppk/js/intro.html">Peter-Paul Koch</a>).</p>
				<pre> 
  function createCookie(name,value,days) {
  if (days) {
    var date = new Date();
    date.setTime(date.getTime()+(days*24*60*60*1000));
    var expires = "; expires="+date.toGMTString();
  }
  else expires = "";
  document.cookie = name+"="+value+expires+"; path=/";
}
function readCookie(name) {
  var nameEQ = name + "=";
  var ca = document.cookie.split(';');
  for(var i=0;i &lt; ca.length;i++) {
    var c = ca[i];
    while (c.charAt(0)==' ') c = c.substring(1,c.length);
    if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length,c.length);
  }
  return null;
}
</pre>
				<p>To use these cookie functions, we need to add <code>onload</code> and <code>onunload</code><a href="http://www.alistapart.com/articles/domtricks2/">event listeners</a> to the window.</p>
				<h3>onLoad</h3>
				<p>There is a w3c specified DOM Level 2 attribute, “disabled,” that is set to false when a style sheet is applied to the document. This attribute is correctly implemented in Mozilla, but unfortunately not in MSIE.</p>
				<p>MSIE <em>does</em> have a proprietary HTML attribute, also called “disabled,” that applies to link elements. This attribute is initially set to false for all link elements.</p>
				<p>To set the MSIE disabled attribute to match the DOM Level 2 disabled attribute, we can call the setActiveStyleSheet() function with the name of the preferred style sheet.</p>
				<p>To find out which style sheet is the preferred style sheet, we need another function. Because this function is so similar to the <code>getActiveStyleSheet()</code> function I’m not going to explain how it works, but here is what it may look like:</p>
				<pre> 
  function getPreferredStyleSheet() {
  var i, a;
  for(i=0; (a = document.getElementsByTagName("link")[i]); i++) {
    if(a.getAttribute("rel").indexOf("style") != -1
       &amp;&amp; a.getAttribute("rel").indexOf("alt") == -1
       &amp;&amp; a.getAttribute("title")
       ) return a.getAttribute("title");
  }
  return null;
}
</pre>
				<p>In the onload function, we first set a title variable. This either holds the value of the previous style sheet that is stored in the cookie, or if there isn’t one, the title of our preferred style sheet. To keep things logical, let’s call the cookie “style.”</p>
				<p>Next we call up the <code>setActiveStyleSheet()</code> function passing the title variable as the title. Our onload function looks something like this:</p>
				<pre> 
  window.onload = function(e) {
  var cookie = readCookie("style");
  var title = cookie ? cookie : getPreferredStyleSheet();
  setActiveStyleSheet(title);
}</pre>
				<p>Note that it may be desirable to call this function before the onload event as well, causing the document to “paint” with our style sheet preference.</p>
				<p>If you choose to do this, make sure the function is called after the functions and the link elements have been defined.</p>
				<h3>onUnload</h3>
				<p>To save the cookie in the onunload event is simpler. All we have to do is use the <code>getActiveStyleSheet()</code> function to return the active style sheet, and save this in a cookie. Using the function to store a cookie we will end up with something like this:</p>
				<pre> 
  window.onunload = function(e) {
  var title = getActiveStyleSheet();
  createCookie("style", title, 365);
}
</pre>
				<h2>Puttin’ it all together</h2>
				<p>To use these functions to make your website more sexy, you need to include them in your document. To make it easy, I have put them all together in a javascript file, ready for you to <a href="http://www.alistapart.com/d/alternate/styleswitcher.js">download</a> and add to your site.</p>
				<h4>
						<a href="http://www.alistapart.com/d/alternate/styleswitcher.js">Download styleswitcher.js</a>
				</h4>
				<p>To include the javascript file, you add a script element to the head of your document, making sure that it is put below all the style sheet link elements you have. The HTML would look like this:</p>
				<pre>&lt;script type="text/javascript" 
src="/scripts/styleswitcher.js"&gt;&lt;/script&gt;
</pre>
				<p>To allow the visitor to change the active style sheet, you could use javascript onClick events. For example, to have the option to switch between two themes with titles “default” and “paul,” you could use the following HTML:</p>
				<pre>&lt;a href="#" 
onclick="setActiveStyleSheet('default'); 
return false;"&gt;change style to default&lt;/a&gt;

&lt;a href="#" 
onclick="setActiveStyleSheet('paul'); 
return false;"&gt;change style to paul&lt;/a&gt;
</pre>
				<p class="vs10">Once the visitor has selected a theme, it will be stored in a cookie. To use the same theme throughout your website, the same style sheet and javascript link elements should be included in the head of every page of the site.</p>
				<h3>That’s all, folks!</h3>
				<p>There you have it, a customizable website that uses link elements to link to style sheets as the W3C has told us we should. Enjoy!</p>
		</div>
<img src ="http://www.blogjava.net/mstar/aggbug/52686.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/mstar/" target="_blank">黑灵</a> 2006-06-14 11:45 <a href="http://www.blogjava.net/mstar/archive/2006/06/14/52686.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java &amp; Rhino &amp; Groovy 性能啊！！</title><link>http://www.blogjava.net/mstar/archive/2006/06/07/51145.html</link><dc:creator>黑灵</dc:creator><author>黑灵</author><pubDate>Wed, 07 Jun 2006 07:44:00 GMT</pubDate><guid>http://www.blogjava.net/mstar/archive/2006/06/07/51145.html</guid><wfw:comment>http://www.blogjava.net/mstar/comments/51145.html</wfw:comment><comments>http://www.blogjava.net/mstar/archive/2006/06/07/51145.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.blogjava.net/mstar/comments/commentRss/51145.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/mstar/services/trackbacks/51145.html</trackback:ping><description><![CDATA[早就听说Groovy性能比Java还要差很多，于是一直没有花心是去研究它，今天在newsgroup中有看到了有人抱怨Groovy的性能。于是自己也做了个测试，同时也把Rhino拉了进来。<br /><br />Rhino是Mozilla的javascript脚本引擎，和Groovy性质差不多，但是没有Groovy功能那么多。<br /><br />代码如下<br />BenchMark.java<br /><div style="border: 1px solid rgb(204, 204, 204); padding: 4px 5px 4px 4px; background-color: rgb(238, 238, 238); font-size: 13px; width: 98%;"><!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--><span style="color: rgb(0, 0, 255);">public</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 0, 255);">class</span><span style="color: rgb(0, 0, 0);"> BenchMark{<br />    </span><span style="color: rgb(0, 0, 255);">public</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 0, 255);">static</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 0, 255);">void</span><span style="color: rgb(0, 0, 0);"> main(String args[]){<br />          </span><span style="color: rgb(0, 0, 255);">long</span><span style="color: rgb(0, 0, 0);"> t</span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);">System.currentTimeMillis();<br />          </span><span style="color: rgb(0, 0, 255);">double</span><span style="color: rgb(0, 0, 0);"> seed</span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);">0.5</span><span style="color: rgb(0, 0, 0);">;<br />          </span><span style="color: rgb(0, 0, 255);">for</span><span style="color: rgb(0, 0, 0);">(</span><span style="color: rgb(0, 0, 255);">int</span><span style="color: rgb(0, 0, 0);"> j</span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);">0</span><span style="color: rgb(0, 0, 0);">; j</span><span style="color: rgb(0, 0, 0);">&lt;</span><span style="color: rgb(0, 0, 0);">20</span><span style="color: rgb(0, 0, 0);">; j</span><span style="color: rgb(0, 0, 0);">++</span><span style="color: rgb(0, 0, 0);">)<br />              </span><span style="color: rgb(0, 0, 255);">for</span><span style="color: rgb(0, 0, 0);">(</span><span style="color: rgb(0, 0, 255);">int</span><span style="color: rgb(0, 0, 0);"> i</span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);">0</span><span style="color: rgb(0, 0, 0);">; i</span><span style="color: rgb(0, 0, 0);">&lt;</span><span style="color: rgb(0, 0, 0);">1000000</span><span style="color: rgb(0, 0, 0);">; i</span><span style="color: rgb(0, 0, 0);">++</span><span style="color: rgb(0, 0, 0);">)<br />              {<br />                  seed</span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);">Math.cos(seed</span><span style="color: rgb(0, 0, 0);">+</span><span style="color: rgb(0, 0, 0);">i);<br />              }<br />          </span><span style="color: rgb(0, 0, 255);">long</span><span style="color: rgb(0, 0, 0);"> t2</span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);">System.currentTimeMillis();<br />          System.out.println(t2</span><span style="color: rgb(0, 0, 0);">-</span><span style="color: rgb(0, 0, 0);">t);<br />    }<br />}</span></div><br />BenchMark.js<br /><div style="border: 1px solid rgb(204, 204, 204); padding: 4px 5px 4px 4px; background-color: rgb(238, 238, 238); font-size: 13px; width: 98%;"><!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--><span style="color: rgb(0, 0, 0);">importPackage(java.lang);<br /></span><span style="color: rgb(0, 0, 255);">var</span><span style="color: rgb(0, 0, 0);"> t </span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);"> System.currentTimeMillis();<br /></span><span style="color: rgb(0, 0, 255);">var</span><span style="color: rgb(0, 0, 0);"> seed </span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 0, 0);">0.5</span><span style="color: rgb(0, 0, 0);">;<br /></span><span style="color: rgb(0, 0, 255);">for</span><span style="color: rgb(0, 0, 0);">(</span><span style="color: rgb(0, 0, 255);">var</span><span style="color: rgb(0, 0, 0);"> j </span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 0, 0);">0</span><span style="color: rgb(0, 0, 0);">; j </span><span style="color: rgb(0, 0, 0);">&lt;</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 0, 0);">20</span><span style="color: rgb(0, 0, 0);">; j</span><span style="color: rgb(0, 0, 0);">++</span><span style="color: rgb(0, 0, 0);">)<br />    </span><span style="color: rgb(0, 0, 255);">for</span><span style="color: rgb(0, 0, 0);">(</span><span style="color: rgb(0, 0, 255);">var</span><span style="color: rgb(0, 0, 0);"> i</span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);">0</span><span style="color: rgb(0, 0, 0);">; i</span><span style="color: rgb(0, 0, 0);">&lt;</span><span style="color: rgb(0, 0, 0);">1000000</span><span style="color: rgb(0, 0, 0);">; i</span><span style="color: rgb(0, 0, 0);">++</span><span style="color: rgb(0, 0, 0);">){<br />      seed</span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);">Math.cos(seed</span><span style="color: rgb(0, 0, 0);">+</span><span style="color: rgb(0, 0, 0);">i);<br />    }<br /></span><span style="color: rgb(0, 0, 255);">var</span><span style="color: rgb(0, 0, 0);"> t2 </span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);"> System.currentTimeMillis();<br />print(t2</span><span style="color: rgb(0, 0, 0);">-</span><span style="color: rgb(0, 0, 0);">t);</span></div><br />BenchMark.groovy<br /><div style="border: 1px solid rgb(204, 204, 204); padding: 4px 5px 4px 4px; background-color: rgb(238, 238, 238); font-size: 13px; width: 98%;"><!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--><span style="color: rgb(0, 0, 255);">long</span><span style="color: rgb(0, 0, 0);"> t</span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);">System.currentTimeMillis();<br /></span><span style="color: rgb(0, 0, 255);">double</span><span style="color: rgb(0, 0, 0);"> seed</span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);">0.5</span><span style="color: rgb(0, 0, 0);"><br /></span><span style="color: rgb(0, 0, 0);">20</span><span style="color: rgb(0, 0, 0);">.times{<br /> </span><span style="color: rgb(0, 0, 0);">1000000</span><span style="color: rgb(0, 0, 0);">.times{i </span><span style="color: rgb(0, 0, 0);">-&gt;</span><span style="color: rgb(0, 0, 0);"> seed </span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);"> Math.cos(seed </span><span style="color: rgb(0, 0, 0);">+</span><span style="color: rgb(0, 0, 0);"> i)}<br />}<br />println System.currentTimeMillis() </span><span style="color: rgb(0, 0, 0);">-</span><span style="color: rgb(0, 0, 0);"> t</span></div><br />我的机器<br />P4 3G 超线程<br />内存 1G（这个测试好像和内存没什么关系）<br /><br />java代码当然需要编译才能运行的，js和groovy是直接解释脚本运行，结果如下<br />Java 3813<br />Rhino 34015<br />Groovy 103422（不知道为什么我这个数值似乎有点比别人的大）<br />然后我把js和groovy编译成class，结果差不多，（其实我的结果是编译后的groovy和js都要比不编译还要慢），所以把它们编译了意义应该不大。<br /><br />虽然我这个测试不具有什么现实意义，但是我还是感觉用Groovy（Grails）去做网站不大可行。倒是可以考虑把它们用在不经常被调用，但是代码却要经常变的地方。<br /><img src ="http://www.blogjava.net/mstar/aggbug/51145.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/mstar/" target="_blank">黑灵</a> 2006-06-07 15:44 <a href="http://www.blogjava.net/mstar/archive/2006/06/07/51145.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[备份]由String类型字符组成的日期的星期</title><link>http://www.blogjava.net/mstar/archive/2006/04/11/40385.html</link><dc:creator>黑灵</dc:creator><author>黑灵</author><pubDate>Tue, 11 Apr 2006 02:14:00 GMT</pubDate><guid>http://www.blogjava.net/mstar/archive/2006/04/11/40385.html</guid><wfw:comment>http://www.blogjava.net/mstar/comments/40385.html</wfw:comment><comments>http://www.blogjava.net/mstar/archive/2006/04/11/40385.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.blogjava.net/mstar/comments/commentRss/40385.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/mstar/services/trackbacks/40385.html</trackback:ping><description><![CDATA[
		<div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee">
				<span style="COLOR: #0000ff">&lt;</span>
				<span style="COLOR: #800000">html</span>
				<span style="COLOR: #0000ff">&gt;</span>
				<span style="COLOR: #000000">
						<br />
				</span>
				<span style="COLOR: #0000ff">&lt;</span>
				<span style="COLOR: #800000">head</span>
				<span style="COLOR: #0000ff">&gt;</span>
				<span style="COLOR: #000000">
						<br />
				</span>
				<span style="COLOR: #0000ff">&lt;</span>
				<span style="COLOR: #800000">title</span>
				<span style="COLOR: #0000ff">&gt;</span>
				<span style="COLOR: #000000">测试网页</span>
				<span style="COLOR: #0000ff">&lt;/</span>
				<span style="COLOR: #800000">title</span>
				<span style="COLOR: #0000ff">&gt;</span>
				<span style="COLOR: #000000">
						<br />
				</span>
				<span style="COLOR: #0000ff">&lt;/</span>
				<span style="COLOR: #800000">head</span>
				<span style="COLOR: #0000ff">&gt;</span>
				<span style="COLOR: #000000">
						<br />
				</span>
				<span style="COLOR: #0000ff">&lt;</span>
				<span style="COLOR: #800000">body</span>
				<span style="COLOR: #0000ff">&gt;</span>
				<span style="COLOR: #000000">
						<br />
				</span>
				<span style="COLOR: #0000ff">&lt;</span>
				<span style="COLOR: #800000">script</span>
				<span style="COLOR: #0000ff">&gt;</span>
				<span style="COLOR: #000000; BACKGROUND-COLOR: #f5f5f5">
						<br />
				</span>
				<span style="COLOR: #0000ff; BACKGROUND-COLOR: #f5f5f5">var</span>
				<span style="COLOR: #000000; BACKGROUND-COLOR: #f5f5f5"> myYr</span>
				<span style="COLOR: #000000; BACKGROUND-COLOR: #f5f5f5">=</span>
				<span style="COLOR: #000000; BACKGROUND-COLOR: #f5f5f5">"</span>
				<span style="COLOR: #000000; BACKGROUND-COLOR: #f5f5f5">2006</span>
				<span style="COLOR: #000000; BACKGROUND-COLOR: #f5f5f5">"</span>
				<span style="COLOR: #000000; BACKGROUND-COLOR: #f5f5f5">;<br /></span>
				<span style="COLOR: #0000ff; BACKGROUND-COLOR: #f5f5f5">var</span>
				<span style="COLOR: #000000; BACKGROUND-COLOR: #f5f5f5"> myMonth</span>
				<span style="COLOR: #000000; BACKGROUND-COLOR: #f5f5f5">=</span>
				<span style="COLOR: #000000; BACKGROUND-COLOR: #f5f5f5">"</span>
				<span style="COLOR: #000000; BACKGROUND-COLOR: #f5f5f5">4</span>
				<span style="COLOR: #000000; BACKGROUND-COLOR: #f5f5f5">"</span>
				<span style="COLOR: #000000; BACKGROUND-COLOR: #f5f5f5">;<br /></span>
				<span style="COLOR: #0000ff; BACKGROUND-COLOR: #f5f5f5">var</span>
				<span style="COLOR: #000000; BACKGROUND-COLOR: #f5f5f5"> myDate</span>
				<span style="COLOR: #000000; BACKGROUND-COLOR: #f5f5f5">=</span>
				<span style="COLOR: #000000; BACKGROUND-COLOR: #f5f5f5">"</span>
				<span style="COLOR: #000000; BACKGROUND-COLOR: #f5f5f5">11</span>
				<span style="COLOR: #000000; BACKGROUND-COLOR: #f5f5f5">"</span>
				<span style="COLOR: #000000; BACKGROUND-COLOR: #f5f5f5">;<br /></span>
				<span style="COLOR: #0000ff; BACKGROUND-COLOR: #f5f5f5">var</span>
				<span style="COLOR: #000000; BACKGROUND-COLOR: #f5f5f5"> strWeek</span>
				<span style="COLOR: #000000; BACKGROUND-COLOR: #f5f5f5">=</span>
				<span style="COLOR: #000000; BACKGROUND-COLOR: #f5f5f5">"</span>
				<span style="COLOR: #000000; BACKGROUND-COLOR: #f5f5f5">星期</span>
				<span style="COLOR: #000000; BACKGROUND-COLOR: #f5f5f5">"</span>
				<span style="COLOR: #000000; BACKGROUND-COLOR: #f5f5f5">+</span>
				<span style="COLOR: #000000; BACKGROUND-COLOR: #f5f5f5">"</span>
				<span style="COLOR: #000000; BACKGROUND-COLOR: #f5f5f5">日一二三四五六</span>
				<span style="COLOR: #000000; BACKGROUND-COLOR: #f5f5f5">"</span>
				<span style="COLOR: #000000; BACKGROUND-COLOR: #f5f5f5">.charAt(</span>
				<span style="COLOR: #0000ff; BACKGROUND-COLOR: #f5f5f5">new</span>
				<span style="COLOR: #000000; BACKGROUND-COLOR: #f5f5f5"> Date(myYr,myMonth</span>
				<span style="COLOR: #000000; BACKGROUND-COLOR: #f5f5f5">-</span>
				<span style="COLOR: #000000; BACKGROUND-COLOR: #f5f5f5">1</span>
				<span style="COLOR: #000000; BACKGROUND-COLOR: #f5f5f5">,myDate).getDay());<br />alert(strWeek);<br /></span>
				<span style="COLOR: #0000ff">&lt;/</span>
				<span style="COLOR: #800000">script</span>
				<span style="COLOR: #0000ff">&gt;</span>
				<span style="COLOR: #000000">
						<br />
				</span>
				<span style="COLOR: #0000ff">&lt;/</span>
				<span style="COLOR: #800000">body</span>
				<span style="COLOR: #0000ff">&gt;</span>
				<span style="COLOR: #000000">
						<br />
				</span>
				<span style="COLOR: #0000ff">&lt;/</span>
				<span style="COLOR: #800000">html</span>
				<span style="COLOR: #0000ff">&gt;</span>
		</div>
		<br />
		<br />我们的4月就是老外的3月啊，所以要想获得4月的月份，就先要减去1<img src ="http://www.blogjava.net/mstar/aggbug/40385.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/mstar/" target="_blank">黑灵</a> 2006-04-11 10:14 <a href="http://www.blogjava.net/mstar/archive/2006/04/11/40385.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[备份]限制用户只能在input里输入中文</title><link>http://www.blogjava.net/mstar/archive/2006/03/22/36902.html</link><dc:creator>黑灵</dc:creator><author>黑灵</author><pubDate>Wed, 22 Mar 2006 11:14:00 GMT</pubDate><guid>http://www.blogjava.net/mstar/archive/2006/03/22/36902.html</guid><wfw:comment>http://www.blogjava.net/mstar/comments/36902.html</wfw:comment><comments>http://www.blogjava.net/mstar/archive/2006/03/22/36902.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/mstar/comments/commentRss/36902.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/mstar/services/trackbacks/36902.html</trackback:ping><description><![CDATA[
		<div style="border: 1px solid rgb(204, 204, 204); padding: 4px 5px 4px 4px; font-size: 13px; width: 98%; background-color: rgb(238, 238, 238);">
				<font size="4">
						<span style="color: rgb(0, 0, 0);">&lt;</span>
						<span style="color: rgb(0, 0, 0);">script language</span>
						<span style="color: rgb(0, 0, 0);">=</span>
						<span style="color: rgb(0, 0, 0);">javascript</span>
						<span style="color: rgb(0, 0, 0);">&gt;</span>
				</font>
				<span style="color: rgb(0, 0, 0);">
						<br />
				</span>
				<font size="4">
						<span style="color: rgb(0, 0, 255);">function</span>
				</font>
				<font size="4">
						<span style="color: rgb(0, 0, 0);"> isChineseChar(strInput){<br />    </span>
						<span style="color: rgb(0, 0, 255);">var</span>
						<span style="color: rgb(0, 0, 0);"> len </span>
						<span style="color: rgb(0, 0, 0);">=</span>
				</font>
				<font size="4">
						<span style="color: rgb(0, 0, 0);"> strInput.length;<br />    </span>
						<span style="color: rgb(0, 0, 255);">if</span>
						<span style="color: rgb(0, 0, 0);">(</span>
						<span style="color: rgb(0, 0, 0);">0</span>
						<span style="color: rgb(0, 0, 0);"> </span>
						<span style="color: rgb(0, 0, 0);">==</span>
				</font>
				<font size="4">
						<span style="color: rgb(0, 0, 0);"> len)<br />        </span>
						<span style="color: rgb(0, 0, 255);">return</span>
						<span style="color: rgb(0, 0, 0);"> </span>
						<span style="color: rgb(0, 0, 255);">false</span>
				</font>
				<font size="4">
						<span style="color: rgb(0, 0, 0);">;<br />    </span>
						<span style="color: rgb(0, 0, 255);">for</span>
						<span style="color: rgb(0, 0, 0);">(</span>
						<span style="color: rgb(0, 0, 255);">var</span>
						<span style="color: rgb(0, 0, 0);"> i</span>
						<span style="color: rgb(0, 0, 0);">=</span>
						<span style="color: rgb(0, 0, 0);">0</span>
						<span style="color: rgb(0, 0, 0);">;i</span>
						<span style="color: rgb(0, 0, 0);">&lt;</span>
						<span style="color: rgb(0, 0, 0);">len;i</span>
						<span style="color: rgb(0, 0, 0);">++</span>
				</font>
				<font size="4">
						<span style="color: rgb(0, 0, 0);">){<br />        </span>
						<span style="color: rgb(0, 0, 255);">if</span>
						<span style="color: rgb(0, 0, 0);"> (strInput.charCodeAt(i)</span>
						<span style="color: rgb(0, 0, 0);">&lt;=</span>
						<span style="color: rgb(0, 0, 0);">255</span>
				</font>
				<font size="4">
						<span style="color: rgb(0, 0, 0);">)<br />        </span>
						<span style="color: rgb(0, 0, 255);">return</span>
						<span style="color: rgb(0, 0, 0);"> </span>
						<span style="color: rgb(0, 0, 255);">false</span>
				</font>
				<font size="4">
						<span style="color: rgb(0, 0, 0);">;<br />    }<br />    </span>
						<span style="color: rgb(0, 0, 255);">return</span>
						<span style="color: rgb(0, 0, 0);"> </span>
						<span style="color: rgb(0, 0, 255);">true</span>
				</font>
				<font size="4">
						<span style="color: rgb(0, 0, 0);">;<br />}<br />alert(isChineseChar(</span>
						<span style="color: rgb(0, 0, 0);">"</span>
						<span style="color: rgb(0, 0, 0);">skdj</span>
						<span style="color: rgb(0, 0, 0);">"</span>
				</font>
				<font size="4">
						<span style="color: rgb(0, 0, 0);">));<br />alert(isChineseChar(</span>
						<span style="color: rgb(0, 0, 0);">"</span>
						<span style="color: rgb(0, 0, 0);">点算法</span>
						<span style="color: rgb(0, 0, 0);">"</span>
				</font>
				<font size="4">
						<span style="color: rgb(0, 0, 0);">));<br />alert(isChineseChar(</span>
						<span style="color: rgb(0, 0, 0);">"</span>
						<span style="color: rgb(0, 0, 0);">点算法sdf</span>
						<span style="color: rgb(0, 0, 0);">"</span>
				</font>
				<font size="4">
						<span style="color: rgb(0, 0, 0);">));<br /></span>
						<span style="color: rgb(0, 0, 0);">&lt;/</span>
						<span style="color: rgb(0, 0, 0);">script</span>
						<span style="color: rgb(0, 0, 0);">&gt;</span>
				</font>
		</div>
		<br />正则表达式<br /><br /><div style="border: 1px solid rgb(204, 204, 204); padding: 4px 5px 4px 4px; font-size: 13px; width: 98%; background-color: rgb(238, 238, 238);"><font size="4"><span style="color: rgb(0, 0, 0);">&lt;</span><span style="color: rgb(0, 0, 0);">input type</span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">text</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);"> onkeyup</span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">value=value.replace(/[^\u4E00-\u9FA5]/g,'')</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);"> onbeforepaste</span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">clipboardData.setData('text', clipboardData.getData('text').replace(/[^\u4E00-\u9FA5]/g,''))</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 0, 0);">&gt;</span></font><span style="color: rgb(0, 0, 0);">      </span></div><img src ="http://www.blogjava.net/mstar/aggbug/36902.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/mstar/" target="_blank">黑灵</a> 2006-03-22 19:14 <a href="http://www.blogjava.net/mstar/archive/2006/03/22/36902.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>实战 Groovy: 用 Groovy 打造服务器端</title><link>http://www.blogjava.net/mstar/archive/2005/06/17/6263.html</link><dc:creator>黑灵</dc:creator><author>黑灵</author><pubDate>Fri, 17 Jun 2005 02:57:00 GMT</pubDate><guid>http://www.blogjava.net/mstar/archive/2005/06/17/6263.html</guid><wfw:comment>http://www.blogjava.net/mstar/comments/6263.html</wfw:comment><comments>http://www.blogjava.net/mstar/archive/2005/06/17/6263.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.blogjava.net/mstar/comments/commentRss/6263.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/mstar/services/trackbacks/6263.html</trackback:ping><description><![CDATA[<!--StartFragment -->&nbsp;
<BLOCKQUOTE><ABSTRACT-EXTENDED>Groovlet 和 GroovyServer Pages（GSP）框架都是建立在 Java Servlet API 基础之上。不过，与 Strut 和 JSF 不同，Groovy 的服务器端实现不意味着适用于所有情况。相反，它提供了一种快速而又方便地开发服务器端应用程序的简化方法。下面请跟随 Groovy 的鼓吹者 Andrew Glover，听听他如何介绍这些框架，并展示它们的应用。</ABSTRACT-EXTENDED></BLOCKQUOTE>
<P>Java 平台为自己赢得了服务器端应用程序开发的首选平台的名声。Servlet 是服务器端 Java 技术的强大支柱，因此有无数的框架是围绕着 Servlet API 建立起来的，其中包括 Strut、JavaServer Faces (JSF) 和 Tapestry。您可能已经猜到，Groovy 也是以 Servlet API 为基础建立起来的框架，不过，这个框架的目的是简化开发。</P>
<P>Groovlet 和 GroovyServer Pages（GSP）框架的目的是提供一种优雅而又简单的平台，将它用于构建复杂程度不高的 Web 应用程序。就像 GroovySql 不是数据库开发的惟一选择一样，Groovlet 框架也<I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">不是</I> 像 Strut 那样具有更丰富功能的框架的替代品。Groovlet 只是开发人员寻求容易配置和产生工作代码的快速方法时的一种选择。</P>
<P>例如，不久前，我需要 —— <I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">快速地</I> —— 提供一个 stub 应用程序，以测试像 <CODE>xml-rpc</CODE> API 这样的客户端。显然可以用一个 servlet 快速编写出所需要的功能，但是我从没想过钻研 Strut，一秒钟也没有。我考虑过使用基本的普通 Java Servlet API 编写 servlet 及其相关的逻辑，但是由于需要尽快地使用这项功能，所以我选择了使用 Groovlet 快速完成它。</P>
<P>很快您就可看到，这种选择是显而易见的。</P>
<P>在深入研究使用 Groovlet 进行编程之前，我想简单介绍一个在示例代码中会用到的 Groovy 特性。在几个月前的 <A href="http://www-128.ibm.com/developerworks/cn/java/j-alj08034/index.html" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">alt.lang.jre: 感受 Groovy</A> 一文中，我第一次介绍了 <CODE>def</CODE> 关键字。 </P>
<P>
<TABLE cellSpacing=0 cellPadding=5 width="30%" align=right border=1>
<TBODY>
<TR>
<TD background=/developerworks/cn/i/bg-gold.gif>
<P><A name=N10071><B>关于本系列</B></A><BR>将任何工具添加到开发实践中的关键是了解什么时候使用它和什么时候不使用它。脚本语言可以为您增加特别强大的工具，但前提是在相关的场景中正确地使用它。因此，<I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">实战 Groovy</I> 的系列文章专门探讨 Groovy 的实际使用，并指导读者什么时候以及如何成功地使用这些工具。</P></TD></TR></TBODY></TABLE></P>
<P><A name=N1007E><SPAN class=atitle2>在脚本中定义函数</SPAN></A><BR>在普通 Java 编程中，方法必须存在于类对象中。事实上，所有行为都必须在类的上下文中定义。不过在 Groovy 中，行为可以在<I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">函数</I> 中定义，而函数可以在类定义之外定义。</P>
<P>这些函数可以直接用名称引用，并且可以在 Groovy 脚本中定义，这样非常有助于它们的重复使用。Groovy 函数需要 <CODE>def</CODE> 关键字，可以将关键字想像为在脚本范围内可用的全局静态方法。因为 Groovy 是动态类型的语言，所以 <CODE>def</CODE> 不需要对参数作任何类型声明，<CODE>def</CODE> 也不需要 <CODE>return</CODE> 语句。</P>
<P>例如，在清单 1 中，我定义了一个简单的函数，它将输出一个集合的内容，而不管这个集合是 <CODE>list</CODE> 还是 <CODE>map</CODE>。然后我定义一个 <CODE>list</CODE>，填充它，并调用我新定义的 <CODE>def</CODE>。之后，我创建一个 <CODE>map</CODE>，并对这个集合做了同样的操作。</P><A name=code1><B>清单 1. 这就是 def!</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
def logCollection(coll){
  counter = 0;
  coll.each{ x | 
    println "${++counter} item: ${x}"
  }
}
lst = [12, 3, "Andy", 'c']
logCollection(lst)
mp = ["name" : "Groovy", "date" : new Date()]
logCollection(mp)
</CODE></PRE></TD></TR></TBODY></TABLE>
<P><CODE>def</CODE> 不需要 <CODE>return</CODE> 语句，因此如果最后一行产生某个值，那么这个值由 <CODE>def</CODE> 返回。例如，在清单 2 中，代码定义了一个 <CODE>def</CODE>，它返回传递进来的变量的名称。我可以编写它，让它带有或者不带 <CODE>return</CODE> 语句，得到的结果是相同的。</P><A name=code2><B>清单 2. 在 def 中 return 语句是可选的</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
def getJavaType(val){
  val.class.getName()
}
tst = "Test"
println getJavaType(tst)
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>在编写简单的脚本时，<CODE>def</CODE> 关键字会非常好用。您很快就会看到，在开发 Groovlet 时，这个关键字也会派上用场。</P>
<P><A name=N100E7><SPAN class=atitle2>Groovlet 和 GSP</SPAN></A><BR>使用 Groovlet 和 GSP 的前提条件相当简单：需要一个 servlet 容器，以及最新、最伟大版本的 Groovy。这些框架的好处是它们通过一个 web.xml 文件将所选模式的所有 URL 映射到特定的 servlet。因此，建立 Groovlet 和 GSP 的实现的第一步是定义一个 Web 应用程序上下文，并更新它的相关 web.xml 文件。这个文件将包括特定的 servlet 类定义以及它们对应的 URL 模式。</P>
<P>我将使用 Apache Jakarta Tomcat，并且已创建了一个名为 <I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">groove</I> 的上下文。目录结构如清单 3 所示：</P><A name=code3><B>清单 3. groove 上下文的目录列表 </B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
./groove:
drwxrwxrwx+   3 aglover  users        0 Jan 19 12:14 WEB-INF
./WEB-INF:
-rwxrwxrwx+   1 aglover  users      906 Jan 16 14:37 web.xml
drwxrwxrwx+   2 aglover  users        0 Jan 19 17:12 lib
./WEB-INF/lib:
-rwxrwxrwx+   1 aglover  users   832173 Jan 16 14:28 groovy-1.0-beta-9.jar
-rwxrwxrwx+   1 aglover  users    26337 Jan 16 14:29 asm-1.5.2.jar
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>在 WEB-INF 目录中要有一个 web.xml 文件，它至少有一个清单 4 中的元素：</P><A name=code4><B>清单 4. 一个完全配置的 web.xml 文件</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
&lt;?xml version="1.0" encoding="ISO-8859-1"?&gt;
&lt;web-app xmlns="http://java.sun.com/xml/ns/j2ee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation=
    "http://java.sun.com/xml/ns/j2ee
    http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
    version="2.4"&gt;
    &lt;servlet&gt;
      &lt;servlet-name&gt;GroovyServlet&lt;/servlet-name&gt;
      &lt;servlet-class&gt;groovy.servlet.GroovyServlet&lt;/servlet-class&gt;
    &lt;/servlet&gt;
    &lt;servlet&gt;
        &lt;servlet-name&gt;GroovyTemplate&lt;/servlet-name&gt;
        &lt;servlet-class&gt;groovy.servlet.TemplateServlet&lt;/servlet-class&gt;    
	&lt;/servlet&gt;
    &lt;servlet-mapping&gt;
        &lt;servlet-name&gt;GroovyServlet&lt;/servlet-name&gt;
        &lt;url-pattern&gt;*.groovy&lt;/url-pattern&gt;
    &lt;/servlet-mapping&gt;
    &lt;servlet-mapping&gt;
        &lt;servlet-name&gt;GroovyTemplate&lt;/servlet-name&gt;
        &lt;url-pattern&gt;*.gsp&lt;/url-pattern&gt;
    &lt;/servlet-mapping&gt;
&lt;/web-app&gt;
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>上述 web.xml 文件中的定义声明了以下内容：所有以 <CODE>.groovy</CODE> 结尾的请求（如 <CODE>http://localhost:8080/groove/hello.groovy</CODE>）都将发送给类 <CODE>groovy.servlet.GroovyServlet</CODE>，而所有以 <CODE>.gsp</CODE> 结尾的请求都将送给类 <CODE>groovy.servlet.TemplateServlet</CODE>。</P>
<P>下一步是将两个 jar 放到 lib 目录中：groovy 发行版本的 jar（在这里是 groovy-1.0-beta-9.jar）和对应的 <CODE>asm</CODE> jar （对于 groovy beta-9 来说是 asm-1.5.2.jar）。</P>
<P>瞧，就是这么简单 —— 我已经准备好了。</P>
<P><A name=N10130><SPAN class=atitle2>Groovlet，请出场</SPAN></A><BR>编写 Groovlet 无疑很简单，因为 Groovy 只有很少的几个对类继承扩展的要求。使用 Groovlet 不需要扩展 <CODE>javax.servlet.http.HttpServlet</CODE>、<CODE>javax.servlet.GenericServlet</CODE> 或者一些华而不实的 <CODE>GroovyServlet</CODE> 类。事实上，创建 Groovlet 就像创建一个 Groovy 脚本一样简单。甚至不必创建一个类。在清单 5 中，我编写了一个简单的 Groovlet，它做两件事：打印一些 HTML，然后提供一些关于它所在的容器的信息。</P><A name=code5><B>清单 5. 开始使用 Groovlet</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
println """
&lt;html&gt;&lt;head&gt;
&lt;title&gt;Groovlets 101&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;p&gt;
Welcome to Groovlets 101. As you can see
this Groovlet is fairly simple.
&lt;/p&gt;
&lt;p&gt;
This course is being run on the following servlet container: &lt;/br&gt;
${application.getServerInfo()}
&lt;/p&gt;
&lt;/body&gt;
&lt;/html&gt;
"""
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>如果在浏览器中观看这个 Groovy，它看起来与图 1 所示类似。</P>
<P><A name=fig1><B>图 1. 简单 Groovlet 的输出</B></A><BR><IMG height=347 alt="" src="http://www-128.ibm.com/developerworks/cn/java/j-pg03155/figure1.jpg" width=600 border=0 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> </P>
<P>仔细观察清单 5 中的 Groovlet，会让您回想起第一次编写 Groovy 脚本的时候。首先，没有 <CODE>main</CODE> 方法或者类定义，只有一些简单的代码。而且，Groovlet 框架隐式地提供实例变量，比如 <CODE>ServletRequest</CODE>、<CODE>ServletResponse</CODE>、<CODE>ServletContext</CODE> 和 <CODE>HttpSession</CODE>。注意我是如何通过 <CODE>application</CODE> 变量引用 <CODE>ServletContext</CODE> 的实例的。如果想获得 <CODE>HttpSession</CODE> 的实例，那么就要使用 <CODE>session</CODE> 变量名。与此类似，可以对 <CODE>ServletRequest</CODE> 和 <CODE>ServletResponse</CODE> 分别使用 <CODE>request</CODE> 和 <CODE>response</CODE>。</P>
<P><A name=N1019C><SPAN class=atitle2>一个诊断 Groovlet</SPAN></A><BR>编写 Groovlet 不仅像创建一个 Groovy 脚本那样简单，而且还可以用 <CODE>def</CODE> 关键字定义函数，并在 Groovlet 中直接调用它们。为了展示这一点，我将创建一个非凡的 Groovlet，它将对 Web 应用程序进行一些诊断。</P>
<P>假设您编写了一个 Web 应用程序，它被世界上不同的客户所购买。您有一个大客户群，并且不断发布这个应用程序有一段时间了。从过去的支持问题中，您注意到许多急切的客户电话都与错误的 JVM 版本和错误的对象关系映射（ORM）所导致的问题有关。</P>
<P>您很忙，所以让我拿出一个解决方案。我用 Groovlet <I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">迅速地</I> 创建了一个简单的诊断脚本，它将验证 VM 版本，并试图创建一个 Hibernate 会话（请参阅<A href="http://www-128.ibm.com/developerworks/cn/java/j-pg03155/index.html#resources" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">参考资料</A>）。我首先创建两个函数，并在浏览器连接脚本时调用它们。清单 6 定义了这个诊断 Groovlet：</P><A name=code6><B>清单 6. 一个诊断 Groovlet</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
import com.vanward.resource.hibernate.factory.DefaultHibernateSessionFactory
/**
 * Tests VM version from environment- note, even 1.5 will
 * cause an assertion error.
 */
def testVMVersion(){
  println "&lt;h3&gt;JVM Version Check: &lt;/h3&gt;"
  vers = System.getProperty("java.version")
  assert vers.startsWith("1.4"): "JVM must be at least 1.4"
  println "&lt;p&gt;JVM version: ${vers} &lt;/p&gt;"
}
/**
 * Attempts to create an instance of a hibernate session. If this
 * works we have a connection to a database; additionally, we 
 * have a properly configured hibernate instance.
 */
def testHibernate(){
  println "&lt;h3&gt;Hibernate Configuration Check: &lt;/h3&gt;"
  try{
    sessFactory = DefaultHibernateSessionFactory.getInstance()
    session = sessFactory.getHibernateSession()
    assert session != null: "Unable to create hibernate session. 
    Session was null"
    println "&lt;p&gt;Hibernate configuration check was successful&lt;/p&gt;"
  }catch(Throwable tr){
    println """
    &lt;p&gt;Unable to create hibernate session. Exception type is: &lt;br/&gt;
    &lt;i&gt;${tr.toString()} &lt;/i&gt;&lt;br/&gt;		
    &lt;/p&gt;
    """
  }   
}
println """
&lt;html&gt;&lt;head&gt;
&lt;title&gt;Diagnostics Check&lt;/title&gt;&lt;/head&gt;
&lt;body&gt;
"""
testVMVersion()
testHibernate()
println """
&lt;/body&gt;&lt;/html&gt;
"""
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>这个 Groovlet 的验证逻辑非常简单，但是它可以完成这项工作。只要将诊断脚本绑定到 web 应用程序即可，当客户服务台收到电话时，它们将指点客户用浏览器访问 <CODE>Diagnostics.groovy</CODE> 脚本，并让这些脚本报告它们的发现。结果可能看起来像图 2 这样。</P>
<P><A name=fig2><B>图 2. 诊断 Groovlet 的输出</B></A><BR><IMG height=346 alt="" src="http://www-128.ibm.com/developerworks/cn/java/j-pg03155/figure2.jpg" width=600 border=0 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> </P>
<P><A name=N101DA><SPAN class=atitle2>那些 GSP 呢？</SPAN></A><BR>到目前为止，我主要关注于编写 Groovlet。不过，正如您将会看到的那样，很容易用 Groovy 的 GSP 页对 Groovlets 框架进行补充，就像 JSP 补充 Servlet API 一样。</P>
<P>表面上，GSP 看起来很像 JSP，实际上它们不可能有太大的差别，因为 GSP 框架其实就是一个模板引擎。如果不熟悉模板引擎，那么可能需要快速地回顾一下<A href="http://www-128.ibm.com/developerworks/cn/java/j-pg02155/index.html" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">上月的文章</A>。</P>
<P>虽然 GSP 和 JSP 是根本不同的技术，但是 GSP 是加入表达 Web 应用程序的视图的很好候选人，这一点它们是类似的。您可能会想起来，在上月的文章中，有一项促进视图的技术可以将应用程序的业务逻辑问题与其相应的视图分离。如果快速查看一下<A href="http://www-128.ibm.com/developerworks/cn/java/j-pg03155/index.html#code6" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">清单 6</A> 中的诊断 Groovlet，就可以看出 GSP 代码改进了哪些地方。</P>
<P>是的，Groovlet 有些简陋，不是吗？问题在于它混合了应用程序逻辑和大量输出 HTML 的 <CODE>println</CODE>。幸运的是，可以通过创建一个简单的 GSP 来补充这个 Groovlet，从而解决这个问题。</P>
<P><A name=N101F8><SPAN class=atitle3>示例 GSP</SPAN></A><BR>创建 GSP 与创建 Groovlet 一样容易。GSP 开发的关键是认识到 GSP 实质上是一个模板，因此，它最适合有限的逻辑。我将在清单 7 中创建一个简单的 GSP 作为开始：</P><A name=code7><B>清单 7. 一个简单的 GSP</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
&lt;html&gt;
&lt;head&gt;&lt;title&gt;index.gsp&lt;/title&gt;&lt;/head&gt;
&lt;body&gt;
&lt;b&gt;&lt;% println "hello gsp" %&gt;&lt;/b&gt;
&lt;p&gt;
&lt;% wrd = "Groovy"
   for (i in wrd){ 
 %&gt;
 &lt;h1&gt; &lt;%=i%&gt; &lt;br/&gt;
   
 &lt;%} %&gt;
&lt;/p&gt;
&lt;/body&gt;
&lt;/html&gt;
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>观察上面的 GSP 可能很容易让您回想起标准 Groovy 模板开发。像 JSP 一样，它使用 <CODE>&lt;%</CODE>， 但是，与 Groovlet 框架类似，它允许您访问常用 servlet 对象，比如 <CODE>ServletRequest</CODE>、<CODE>ServletResponse</CODE>、<CODE>ServletContext</CODE> 和 <CODE>HttpSession</CODE> 对象。</P>
<P><A name=N10223><SPAN class=atitle2>重构应用程序 ...</SPAN></A><BR>在练习编程语言或者平台发展的时候，重构老的代码可以学到很多东西。我将重构<A href="http://www-128.ibm.com/developerworks/cn/java/j-pg01115.html" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">一月份专栏</A>中的简单报告应用程序，那时候您才刚开始学习 GroovySql。</P>
<P>您还记得吗，我构建了一个快速但不完善的报告应用程序，它可以在组织中有多次使用。但结果是，它变成了研究公司数据库活动的相当流行的应用程序。现在，非技术人员希望可以访问这个巨大的报告，但是他们不想很费事地在自已的计算机上安装 Groovy 来运行它。</P>
<P>我多少预计到了这种情况的发生，解决方案<I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">实际上</I> 是显而易见的：让报告应用程序支持 Web。很幸运，Groovlet 和 GSP 使重构变成小事一桩。</P>
<P><A name=N10239><SPAN class=atitle3>重构报告应用程序</SPAN></A><BR>首先，我将处理 <A href="http://www-128.ibm.com/developerworks/cn/java/j-pg01115.html#%20code12" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">&gt; GroovySql 一文的清单 12</A> 中的简单应用程序。重构这个应用程序很容易：只要将所有 <CODE>println</CODE> 替换成用 <CODE>setAttribute()</CODE> 方法，然后将实例变量放入 <CODE>HttpRequest</CODE> 对象的逻辑中即可。</P>
<P>下一步是用 <CODE>RequestDispatcher</CODE> 将 <CODE>request</CODE> 转发给 GSP，它会处理报告应用程序的视图部分。清单 8 定义了新的报告 Groovlet：</P><A name=code8><B>清单 8. 重构后的数据库报告应用程序</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
import groovy.sql.Sql
/**
 * forwards to passed in page
 */
def forward(page, req, res){
  dis = req.getRequestDispatcher(page);
  dis.forward(req, res);
}
sql = Sql.newInstance("jdbc:mysql://yourserver.anywhere/tiger", "scott",
        "tiger", "org.gjt.mm.mysql.Driver")
   
uptime = null
questions = null
insertnum = null
selectnum = null
updatenum = null
sql.eachRow("show status"){ status |
  if(status.variable_name == "Uptime"){
         uptime =  status[1]
	 request.setAttribute("uptime", uptime)
  }else if (status.variable_name == "Questions"){
         questions =  status[1]
	 request.setAttribute("questions", questions)
  }
}
request.setAttribute("qpm", Integer.valueOf(questions) / 
Integer.valueOf(uptime) )
sql.eachRow("show status like 'Com_%'"){ status |
    if(status.variable_name == "Com_insert"){
         insertnum =  Integer.valueOf(status[1])
    }else if (status.variable_name == "Com_select"){
         selectnum =  Integer.valueOf(status[1])
    }else if (status.variable_name == "Com_update"){
          updatenum =  Integer.valueOf(status[1])
    }
}
request.setAttribute("qinsert", 100 * (insertnum / Integer.valueOf(uptime)))
request.setAttribute("qselect", 100 * (selectnum / Integer.valueOf(uptime)))
request.setAttribute("qupdate", 100 * (updatenum / Integer.valueOf(uptime)))
forward("mysqlreport.gsp", request, response)
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>清单 8 中的代码应当是您相当熟悉的。我只是将以前应用程序中的所有 <CODE>println</CODE> 替换掉，并添加了 <CODE>forward</CODE> 函数来处理报告的视图部分。</P>
<P><A name=N10273><SPAN class=atitle3>添加视图部分</SPAN></A><BR>下一步是创建 GSP 来处理报告应用程序的视图。因为我是工程师而不是一个艺术家，所以我的视图是相当简单的 —— 一些 HTML 加上一个表，如清单 9 所示：</P><A name=code9><B>清单 9. 报告的视图部分</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
&lt;html&gt;&lt;head&gt;
&lt;title&gt;MySql Health Report&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;table&gt;
&lt;tr&gt;
  &lt;td&gt;Database Uptime:&lt;/td&gt;&lt;td&gt;&lt;% println
  "${request.getAttribute("uptime")}" %&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;Number of Queries:&lt;/td&gt;&lt;td&gt;&lt;% println
  "${request.getAttribute("questions")}" %&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;Queries per Minute =&lt;/td&gt;&lt;td&gt;&lt;% println
  "${request.getAttribute("qpm")}" %&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;% Queries Inserts =&lt;/td&gt;&lt;td&gt;&lt;% println
  "${request.getAttribute("qinsert")}" %&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;% Queries Selects =&lt;/td&gt;&lt;td&gt;&lt;% println
  "${request.getAttribute("qselect")}" %&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;% Queries Updates =&lt;/td&gt;&lt;td&gt;&lt;% println
  "${request.getAttribute("qupdate")}" %&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
&lt;/body&gt;
&lt;/html&gt;
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>运行新的报告应当生成如图 3 所示的输出，数字会有变化。</P>
<P><A name=fig3><B>图 3. 重构后的报告应用程序的输出</B></A><BR><IMG height=347 alt="" src="http://www-128.ibm.com/developerworks/cn/java/j-pg03155/figure3.jpg" width=600 border=0 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> </P>
<P><A name=N1029C><SPAN class=atitle2>结束语</SPAN></A><BR>如您所见，当所需要的功能相当简单并且需要尽快完成时，Groovlet 和 GSP 是进行服务器端开发的当然之选。这两个框架都特别灵活，并且其代码到视图的转化时间事实上是无可匹敌的。</P>
<P>不过，需要强调的是，Groovlet 不是 Strut 的替代品。GSP 框架不是<I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">直接</I> 在速度上与其他产品竞争。GroovySql 不是 Hibernate 的替代品。而 Groovy 也不是 Java 语言的替代品。</P>
<P>无论如何，这些技术是补充，在大多数情况下，Groovy 是快速开发的更简单的一种选择。就像 GroovySql 是直接使用 JDBC 的替代方法一样，Groovlet 和 GSP 实际上是直接使用 Servlet API 的替代品。</P>
<P>下个月，我将探讨 GroovyMarkup 的奇妙世界。</P><img src ="http://www.blogjava.net/mstar/aggbug/6263.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/mstar/" target="_blank">黑灵</a> 2005-06-17 10:57 <a href="http://www.blogjava.net/mstar/archive/2005/06/17/6263.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>实战 Groovy: 用 Groovy 更迅速地对 Java 代码进行单元测试</title><link>http://www.blogjava.net/mstar/archive/2005/06/17/6262.html</link><dc:creator>黑灵</dc:creator><author>黑灵</author><pubDate>Fri, 17 Jun 2005 02:53:00 GMT</pubDate><guid>http://www.blogjava.net/mstar/archive/2005/06/17/6262.html</guid><wfw:comment>http://www.blogjava.net/mstar/comments/6262.html</wfw:comment><comments>http://www.blogjava.net/mstar/archive/2005/06/17/6262.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/mstar/comments/commentRss/6262.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/mstar/services/trackbacks/6262.html</trackback:ping><description><![CDATA[<!--StartFragment -->&nbsp;
<BLOCKQUOTE><ABSTRACT-EXTENDED>不久以前，developerWorks 的作者 Andrew Glover 撰写了一篇介绍 Groovy 的文章，该文章是 <I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">alt.lang.jre</I> 系列的一部分，而 Groovy 是一个新提议的用于 Java 平台的标准语言。读者对这篇文章的反应非常热烈，所以我们决定开办这个专栏，提供使用这项热门新技术的实用指导。本文是第一期，将介绍使用 Groovy 和 JUnit 对 Java 代码进行单元测试的一个简单策略。 </ABSTRACT-EXTENDED></BLOCKQUOTE>
<P>开始之前，我首先要招认：我是一个单元测试狂。实际上，我总是无法编写足够的单元测试。如果我相当长一段时间都在进行开发，而 <I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">没有</I>编写相应的单元测试，我就会觉得紧张。单元测试给我信心，让我相信我的代码能够工作，而且我只要看一下，可以修改它，就不会害怕它会崩溃。 </P>
<P>而且，作为一个单元测试狂，我喜欢编写多余的测试用例。但是，我的兴奋不是来自 <I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">编写</I>测试用例，而是 <I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">看着</I>它们生效。所以，如果我能用更快的方式编写测试，我就能更迅速地看到它们的结果。这让我感觉更好。更快一些！ </P>
<P>后来，我找到了 Groovy，它满足了我的单元测试狂，而且至今为止，对我仍然有效。这种新语言给单元测试带来的灵活性，非常令人兴奋，值得认真研究。本文是介绍 Groovy 实践方面的新系列的第一部分，在文中，我将向您介绍使用 Groovy 进行单元测试的快乐。我从概述开始，概述 Groovy 对 Java 平台上的开发所做的独特贡献，然后转而讨论使用 Groovy 和 JUnit 进行单元测试的细节，其中重点放在 Groovy 对 JUnit 的 <CODE>TestCase</CODE> 类的扩展上。最后，我用一个实用的示例进行总结，用第一手材料向您展示如何把 <I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">groovy</I> 的这些特性与 Eclipse 和 Maven 集成在一起。 </P>
<P><A name=N10061><SPAN class=atitle2>不要再坚持 Java 纯粹主义了！</SPAN></A><BR>在我开始介绍用 Groovy 进行单元测试的实际经验之前，我认为先谈谈一个更具一般性的问题 —— 它在您的开发工具箱中的位置，这非常重要。事实是，Groovy 不仅是运行在 Java 运行时环境（JRE）中的脚本语言，它还被提议作为用于 Java 平台的标准语言。正如您们之中的人已经从 <I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">alt.lang.jre</I> 系列（请参阅 <A href="http://www-128.ibm.com/developerworks/cn/java/j-pg11094/index.html#resources" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">参考资料</A>）中学习到的，在为 Java 平台进行脚本编程的时候，有无数的选择，其中大多数是面向快速应用程序开发的高度灵活的环境。 </P>
<P>虽然有这么丰富的选择，但还是有许多开发人选择坚持自己喜欢的、最熟悉的范式：Java 语言。虽然大多数情况下，Java 编程都是很好的选择，但是它有一个非常重要的缺点，蒙住了只看见 Java 的好处的这些人的眼睛。正如一个智者曾经指出的： <I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">如果您仅有的一个工具是一把锤子，那么您看每个问题时都会觉得它像是钉子</I>。我认为这句谚语道出了适用于软件开发的许多事实。 </P>
<P>虽然我希望用这个系列说服您 Java 不是也不应当是开发应用程序的惟一选择，但该脚本确实既有适用的地方也有不适用的地方。专家和新手的区别在于：知道什么时候 <I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">运用</I>该脚本，什么时候 <I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">避免</I>使用它。 </P>
<P>
<TABLE cellSpacing=0 cellPadding=5 width="30%" align=right border=1>
<TBODY>
<TR>
<TD background=/developerworks/cn/i/bg-gold.gif>
<P><A name=N10082><B>关于本系列</B></A><BR>把工具整合到开发实践中的关键是了解什么时候使用它，以及什么时候把它留在工具箱中。脚本语言能够成为工具包中极为强大的附件，但是只有正确地应用在适当的场合时才是这样。为了实现 <I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">实战 Groovy</I> 系列文章这个目标，我专门研究了 Groovy 的一些实战，教给您什么时候怎样才能成功地应用它们。 </P></TD></TR></TBODY></TABLE></P>
<P>例如，对于高性能、事务密集型、企业级应用程序，Groovy 脚本通常不太适合；在这些情况下，您最好的选择 <I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">可能</I>是普通的 J2EE 系统。但另一方面，一些脚本 —— 特别是用 Groovy 编写的脚本 —— 会非常有用，因为它能迅速地为小型的、非常特殊的、不是性能密集型的应用程序（例如配置系统/生成系统）快速制作原型。对于报表应用程序来说， Groovy 脚本也是近乎完美的选择，而最重要的是，对单元测试更是如此。 </P>
<P><A name=N10095><SPAN class=atitle2>为什么用 Groovy 进行单元测试？</SPAN></A><BR>是什么让 Groovy 比起其他脚本平台显得更具有吸引力呢？是它与Java 平台无缝的集成。还是因为它是基于 Java 的语言（不像其他语言，是对 JRE 的替代，因此可能基于旧版的处理器），对于 Java 开发人员来说，Groovy 意味着一条短得让人难以置信的学习曲线。而且一旦将这条学习曲线拉直，Groovy 就能提供一个无与伦比的快速开发平台。</P>
<P>从这个角度来说，Groovy 成功的秘密，在于它的语法 <I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">就是</I> Java 语法，但是规则更少。例如，Groovy 不要求使用分号，变量类型和访问修饰符也是可选的。而且，Groovy 利用了标准的 Java 库，这些都是您已经很熟悉的，包括 <CODE>Collections</CODE> 和 <CODE>File/IO</CODE>。而且，您还可以利用任何 Groovy 提供的 Java 库，包括 JUnit。 </P>
<P>事实上，令人放松的类 Java 语法、对标准 Java 库的重用以及快捷的生成-运行周期，这些都使 Groovy 成为快速开发单元测试的理想替代品。但是会说的不如会做的，还是让我们在代码中看看它的实际效果！</P>
<P><A name=N100AF><SPAN class=atitle2>JUnit 和 Groovy</SPAN></A><BR>用 Groovy 对 Java 代码进行单元测试简单得不能再简单了，有很多入门选择。最直接的选择是沿袭行业标准 —— JUnit。Unit 的简单性和其功能的强大都是无与伦比的，作为非常有帮助的 Java 开发工具，它的普遍性也是无与伦比的，而且没有什么能够阻挡 JUnit 和 Groovy 结合，所以为什么多此一举呢？实际上，只要您看过 JUnit 和 Groovy 在一起工作，我敢打赌您就永远再也不会回头！在这里，要记住的关键的事，您在 Java 语言中能用 JUnit 做到的事，在 Groovy 中用 JUnit 也全都能做到；但是需要的代码要少得多。</P>
<P><A name=N100B8><SPAN class=atitle3>入门</SPAN></A><BR>在您下载了 JUnit 和 Groovy（请参阅 <A href="http://www-128.ibm.com/developerworks/cn/java/j-pg11094/index.html#resources" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">参考资料</A>）之后，您将有两个选择。第一个选择是编写普通的 JUnit 测试用例，就像以前一直做的那样，扩展 JUnit 令人称赞的 <CODE>TestCase</CODE>。第二个选择是应用 Groovy 简洁的 <CODE>GroovyTestCase</CODE> 扩展，它会扩展 JUnit 的 <CODE>TestCase</CODE>。第一个选项是您最快的成功途径，它拥有最多 <I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">与 Java 类似的</I>相似性。而另一方面，第二个选择则把您推进了 Groovey 的世界，这个选择有最大的敏捷性。 </P>
<P>开始的时候，我们来想像一个 Java 对象，该对象对指定的 <CODE>string</CODE> 应用了一个过滤器，并根据匹配结果返回 <CODE>boolean</CODE> 值。该过滤器可以是简单的 <CODE>string</CODE> 操作，例如 <CODE>indexOf()</CODE>，也可以更强大一些，是正则表达式。可能要通过 <CODE>setFilter()</CODE> 方法在运行时设置将使用的过滤器， <CODE>apply()</CODE> 方法接受要过滤的 <CODE>string</CODE>。清单 1 用普通的 Java 代码显示了这个示例的 <CODE>Filter</CODE> 接口： </P><A name=code16><B>清单 1. 一个简单的 Java Filter 接口</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
public interface Filter {
  void setFilter(String fltr);  
  boolean applyFilter(String value);
}
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>我们的想法是用这个特性从大的列表中过滤掉想要的或者不想要的包名。所以，我建立了两个实现： <CODE>RegexPackageFilter</CODE> 和 <CODE>SimplePackageFilter</CODE>。 </P>
<P>把 Groovy 和 JUnit 的强大功能与简单性结合在一起，就形成了如清单 2 所示的简洁的测试套件：</P><A name=code16><B>清单 2. 用 JUunit 制作的 Groovy RegexFilterTest </B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
import junit.framework.TestCase
import com.vanward.sedona.frmwrk.filter.impl.RegexPackageFilter

class RegexFilterTest extends TestCase {  

  void testSimpleRegex() {
    fltr = new RegexPackageFilter()
    fltr.setFilter("java.*")
    val = fltr.applyFilter("java.lang.String")		
    assertEquals("value should be true", true, val)		
  }
}
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>不管您是否熟悉 Groovy，清单 2 中的代码对您来说应当很面熟，因为它只不过是没有分号、访问修饰符或变量类型的 Java 代码而已！上面的 JUnit 测试中有一个测试用例 <CODE>testSimpleRegex()</CODE>，它试图断言 <CODE>RegexPackageFilter</CODE> 用正则表达式 <CODE>"java.*"</CODE> 正确地找到了与 “ <CODE>java.lang.String</CODE>”匹配的对象。 </P>
<P><A name=N10130><SPAN class=atitle2>Groovy 扩展了 JUnit</SPAN></A><BR>扩展 JUnit 的 <CODE>TestCase</CODE> 类，加入附加特性，实际上是每个 JUnit 扩展通常采用的技术。例如，DbUnit 框架（请参阅 <A href="http://www-128.ibm.com/developerworks/cn/java/j-pg11094/index.html#resources" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">参考资料</A>）提供了一个方便的 <CODE>DatabaseTestCase</CODE> 类，能够比以往任何时候都更容易地管理数据库的状态，还有重要的 <CODE>MockStrutsTestCase</CODE>（来自 StrutsTestCase 框架；请参阅 <A href="http://www-128.ibm.com/developerworks/cn/java/j-pg11094/index.html#resources" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">参考资料</A>），它生成虚拟的 <CODE>servlet</CODE> 容器，用来执行 struts 代码。这两个强大的框架都极好地扩展了 JUnit，提供了 JUnit 核心代码中所没有的其他特性；而现在 Groovy 来了，它也是这么做的！ </P>
<P>与 StrutsTestCase 和 DbUnit 一样，Groovy 对 JUnit 的 <CODE>TestCase</CODE> 的扩展给您的工具箱带来了一些重要的新特性。这个特殊的扩展允许您通过 <CODE>groovy</CODE> 命令运行测试套件，而且提供了一套新的 <CODE>assert</CODE> 方法。可以用这些方法很方便地断言脚本的运行是否正确，以及断言各种数组类型的长度和内容等。 </P>
<P><A name=N10160><SPAN class=atitle2>享受 GroovyTestCase 的快乐</SPAN></A><BR>了解 <CODE>GroovyTestCase</CODE> 的能力最好的办法，莫过于实际看到它的效果。在清单 3 中，我已经编写了一个新的 <CODE>SimpleFilterTest</CODE>，但是这次我要扩展 <CODE>GroovyTestCase</CODE> 来实现它： </P><A name=code16><B>清单 3. 一个真正的 GroovyTestCase</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
import groovy.util.GroovyTestCase
import com.vanward.sedona.frmwrk.filter.impl.SimplePackageFilter

class SimpleFilterTest extends GroovyTestCase {
	
  void testSimpleJavaPackage() {
    fltr = new SimplePackageFilter()
    fltr.setFilter("java.")		
    val = fltr.applyFilter("java.lang.String")		
    assertEquals("value should be true", true, val)
  }	
}
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>请注意，可以通过命令行来运行该测试套件，没有运行基于 Java 的 JUnit 测试套件所需要的 <CODE>main()</CODE> 方法。实际上，如果我用 Java 代码编写上面的 <CODE>SimpleFilterTest</CODE>，那么代码看起来会像清单 4 所示的那样： </P><A name=code16><B>清单 4. 用 Java 代码编写的同样的测试用例</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
import junit.framework.TestCase;
import com.vanward.sedona.frmwrk.filter.Filter;
import com.vanward.sedona.frmwrk.filter.impl.SimplePackageFilter;

public class SimplePackageFilterTest extends TestCase {       

   public void testSimpleRegex() {
	Filter fltr = new SimplePackageFilter();
	fltr.setFilter("java.");
	boolean val = fltr.applyFilter("java.lang.String");
	assertEquals("value should be true", true, val);
   }
	
   public static void main(String[] args) {
 	junit.textui.TestRunner.run(SimplePackageFilterTest.class);
   }
}
</CODE></PRE></TD></TR></TBODY></TABLE>
<P><A name=N10196><SPAN class=atitle3>用断言进行测试</SPAN></A><BR>除了可以让您通过命令行运行测试之外， <CODE>GroovyTestCase</CODE> 还向您提供了一些特别方便的 <CODE>assert</CODE> 方法。例如， <CODE>assertArrayEquals</CODE>，它可以检查两个数据中对应的每一个值和各自的长度，从而断言这两个数据是否相等。从清单 5 的示例开始，就可以看到 Groovy 断言的实际效果，清单 5 是一个简洁的基于 Java 的方法，它把 <CODE>string</CODE> 分解成数组。（请注意，我可能使用了 Java 1.4 中新添加的 <CODE>string</CODE> 特性编写以下的示例类。我采用 Jakarta Commons <CODE>StringUtils</CODE> 类来确保与 Java 1.3 的后向兼容性。） </P><A name=code16><B>清单 5. 定义一个 Java StringSplitter 类</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
import org.apache.commons.lang.StringUtils;

public class StringSplitter {
  public static String[] split(final String input, final String separator){
   return StringUtils.split(input, separator);
  }
}
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>清单 6 展示了用 Groovy 测试套件及其对应的 <CODE>assertArrayEquals</CODE> 方法对这个类进行测试是多么简单： </P><A name=code16><B>清单 6. 使用 GroovyTestCase 的 assertArrayEquals 方法</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
import groovy.util.GroovyTestCase
import com.vanward.resource.string.StringSplitter

class StringSplitTest extends GroovyTestCase {
	
  void testFullSplit() {
    splitAr = StringSplitter.split("groovy.util.GroovyTestCase", ".")		
    expect = ["groovy", "util", "GroovyTestCase"].toArray()
    assertArrayEquals(expect, splitAr)		
  }	
}
</CODE></PRE></TD></TR></TBODY></TABLE>
<P><A name=N101D4><SPAN class=atitle2>其他方法</SPAN></A><BR>Groovy 可以让您单独或成批运行测试。使用 <CODE>GroovyTestCase</CODE> 扩展，运行单个测试毫不费力。只要运行 <CODE>groovy</CODE> 命令，后面跟着要运行的测试套件即可，如清单 7 所示： </P><A name=code16><B>清单 7. 通过 groovy 命令运行 GroovyTestCase 测试用例</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
$./groovy test/com/vanward/sedona/frmwrk/filter/impl/SimpleFilterTest.groovy
.
Time: 0.047

OK (1 test)
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>Groovy 还提供了一个标准的 JUnit 测试套件，叫作 <CODE>GroovyTestSuite</CODE>。只要运行该测试套件，把脚本的路径传给它，它就会运行脚本，就像 <CODE>groovy</CODE> 命令一样。这项技术的好处是，它可以让您在 IDE 中运行脚本。例如，在 Eclipse 中，我只是为示例项目建立了一个新的运行配置（一定要选中 “Include external jars when searching for a main class”），然后找到主类 <CODE>groovy.util.GroovyTestSuite</CODE>，如图 1 所示： </P>
<P><A name=N10201><B>图 1. 用 Eclipse 运行 GroovyTestSuite</B></A><BR><IMG height=387 alt="图 1. 用 Eclipse 运行 GroovyTestSuite" src="http://www-128.ibm.com/developerworks/cn/java/j-pg11094/pic1.jpg" width=600 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> </P>
<P>在图 2 中，您可以看到当点击 Arguments 标签，写入脚本的路径时，会发生了什么：</P>
<P><A name=N10215><B>图 2. 在 Eclipse 中指定脚本的路径</B></A><BR><IMG height=389 alt="图 2. 在 Eclipse 中指定脚本的路径" src="http://www-128.ibm.com/developerworks/cn/java/j-pg11094/pic2.jpg" width=600 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> </P>
<P>运行一个自己喜欢的 JUnit Groovy 脚本，实在是很简单，只要在 Eclipse 中找到对应的运行配置就可以了。</P>
<P><A name=N10227><SPAN class=atitle2>用 Ant 和 Maven 进行测试</SPAN></A><BR>这个像 JUnit 一样的框架的美妙之处还在于，它可以把整套测试作为 build 的一部分运行，不需要人工进行干预。随着越来越多的人把测试用例加入代码基（code base），整体的测试套件日益增长，形成一个极好的回归平台（regression platform）。更妙的是，Ant 和 Maven 这样的 build 框架已经加入了报告特性，可以归纳 Junit 批处理任务运行的结果。</P>
<P>把一组 Groovy 测试用例整合到某一个构建中的最简单的方法是把它们编译成普通的 Java 字节码，然后把它们包含在 Ant 和 Maven 提供的标准的 Junit 批命令中。幸运的是，Groovy 提供了一个 Ant 标签，能够把未编译的 Groovy 脚本集成到字节码中，这样，把脚本转换成有用的字节码的处理工作就变得再简单不过。例如，如果正在使用 Maven 进行构建工作，那么只需在maven.xml 文件中添加两个新的目标、在 project.xml 中添加两个新的相关性、在 build.properties 文件中添加一个简单的标志就可以了。</P>
<P>我要从更新 maven.xml 文件开始，用新的目标来编译示例脚本，如清单 8 所示：</P><A name=code16><B>清单 8. 定义 Groovyc 目标的新 maven.xml 文件</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
 &lt;goal name="run-groovyc" prereqs="java:compile,test:compile"&gt;
   
   &lt;path id="groovy.classpath"&gt;
     &lt;pathelement path="${maven.build.dest}"/&gt;
     &lt;pathelement path="target/classes"/&gt;
     &lt;pathelement path="target/test-classes"/&gt;
     &lt;path refid="maven.dependency.classpath"/&gt;
   &lt;/path&gt;

 &lt;taskdef name="groovyc" classname="org.codehaus.groovy.ant.Groovyc"&gt;
    &lt;classpath refid="groovy.classpath"/&gt;
 &lt;/taskdef&gt;

 &lt;groovyc destdir="${basedir}/target/test-classes" srcdir="${basedir}/test/groovy" 
          listfiles="true"&gt;
	&lt;classpath refid="groovy.classpath"/&gt;
 &lt;/groovyc&gt;

 &lt;/goal&gt;
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>上面代码中发生了以下几件事。第一，我定义一个名为 <CODE>run-groovyc</CODE> 的新目标。该目标有两个前提条件， <CODE>java:compile</CODE> 编译示例源代码， <CODE>test:compile</CODE> 编译普通的 Java-JUnit 类。我还用 <CODE>&lt;path&gt;</CODE> 标签创建了一个 classpath。在该例中，classpath 把 build 目录（保存编译后的源文件）和与它相关的所有依存关系（即 JAR 文件）整合在一起。接着，我还用 <CODE>&lt;taskdef&gt;</CODE> Ant 标签定义了 <CODE>groovyc</CODE> 任务。 </P>
<P>而且，请您注意我在 classpath 中是如何告诉 Maven 到哪里去找 <CODE>org.codehaus.groovy.ant.Groovyc</CODE> 这个类。在示例的最后一行，我定义了 <CODE>&lt;groovyc&gt;</CODE> 标签，它会编译在 <CODE>test/groovy</CODE> 目录中发现的全部 Groovy 脚本，并把生成的 <I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">.class</I> 文件放在 <CODE>target/test-classes</CODE> 目录中。 </P>
<P><A name=N10272><SPAN class=atitle3>一些重要细节</SPAN></A><BR>为了编译 Groovy 脚本，并运行生成的字节码，我必须要通过 project.xml 文件定义两个新的依存关系（ <I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">groovy</I> 和 <I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">asm</I>），如清单 9 所示： </P><A name=code16><B>清单 9. project.xml 文件中的新的依存关系</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
  &lt;dependency&gt;
    &lt;groupId&gt;groovy&lt;/groupId&gt;
    &lt;id&gt;groovy&lt;/id&gt;
    &lt;version&gt;1.0-beta-6&lt;/version&gt;
  &lt;/dependency&gt;

  &lt;dependency&gt;
    &lt;groupId&gt;asm&lt;/groupId&gt;
    &lt;id&gt;asm&lt;/id&gt;
    &lt;version&gt;1.4.1&lt;/version&gt;
  &lt;/dependency&gt;
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>一旦将脚本编译成普遍的 Java 字节码，那么任何标准的 JUnit 运行器就都能运行它们。因为 Ant 和 Maven 都拥有 JUnit 运行器标签，所以下一步就是让 JUnit 挑选新编译的 Groovy 脚本。而且，因为 Maven 的 JUnit 运行器使用模式匹配来查找要运行的测试套件，所以需要在 build.properties 文件中添加一个特殊标记，如清单 10 所示，该标记告诉 Maven 去搜索类而不是搜索 .java 文件。</P><A name=code16><B>清单 10. Maven 项目的 build.properties 文件</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
 maven.test.search.classdir = true
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>最后，我在 maven.xml 文件中定义了一个测试目标（ <CODE>goal</CODE>），如清单 11 所示。这样做可以确保 在单元测试运行之前，使用新的 <CODE>run-groovyc</CODE> 目标编译 Groovy 脚本。 </P><A name=code16><B>清单 11. maven.xml 的新目标</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
  &lt;goal name="test"&gt;
    &lt;attainGoal name="run-groovyc"/&gt;
    &lt;attainGoal name="test:test"/&gt;    	
  &lt;/goal&gt;
</CODE></PRE></TD></TR></TBODY></TABLE>
<P><A name=N102B0><SPAN class=atitle3>最后一个，但并非最不重要</SPAN></A><BR>有了新定义的两个目标（一个用来编译脚本，另外一个用来运行 Java 和 Groovy 组合而成的单元测试），剩下的事就只有运行它们，检查是不是每件事都顺利运行！</P>
<P>在清单 12 中，您可以看到，当我运行 Maven，给 <CODE>test</CODE> 传递目标之后，会发生了什么，它首先包含 <CODE>run-groovyc</CODE> 目标（而该目标恰好还包含 <CODE>java:compile</CODE> 和 <CODE>test:compile</CODE> 这两个目标），然后包含 Maven 中自带的标准的 <CODE>test:test</CODE> 目标。请注意观察目标 <CODE>test:test</CODE> 是如何处理新生成的 Groovy 脚本（在该例中，是新 <I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">编译的</I> Groovy 脚本） <I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">以及</I>普通的 Java JUnit 测试。 </P><A name=code16><B>清单 12. 运行新的测试目标</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
$ ./maven test

test:
java:compile:
    [echo] Compiling to /home/aglover/dev/target/classes
    [javac] Compiling 15 source files to /home/aglover/dev/target/classes

test:compile:
    [javac] Compiling 4 source files to /home/aglover/dev/target/test-classes

run-groovyc:
    [groovyc] Compiling 2 source files to /home/aglover/dev/target/test-classes
    [groovyc] /home/aglover/dev/test/groovy/test/RegexFilterTest.groovy
    [groovyc] /home/aglover/dev/test/groovy/test/SimpleFilterTest.groovy

test:test:    
    [junit] Running test.RegexFilterTest
    [junit] Tests run: 1, Failures: 0, Errors: 0, Time elapsed: 0.656 sec    
    [junit] Running test.SimpleFilterTest
    [junit] Tests run: 1, Failures: 0, Errors: 0, Time elapsed: 0.609 sec
    [junit] Running test.SimplePackageFilterTest
    [junit] Tests run: 1, Failures: 0, Errors: 0, Time elapsed: 0.578 sec    
BUILD SUCCESSFUL
Total time: 42 seconds
Finished at: Tue Sep 21 17:37:08 EDT 2004
</CODE></PRE></TD></TR></TBODY></TABLE>
<P><A name=N102E5><SPAN class=atitle2>结束语</SPAN></A><BR>在 <I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">实战 Groovy</I> 系列的第一期中，您学习了 Groovy 这个令人兴奋的脚本语言最实用的应用当中的一个。对于越来越多开发人员而言，单元测试是开发过程的重要组成部分；而使用 Groovy 和 JUnit 对 Java 代码进行测试就变成了轻而易举的事情。 </P>
<P>Groovy 简单的语法、内部的灵活性，使其成为迅速编写有效的 JUnit 测试、将测试整合到自动编译中的一个优秀平台。对于像我一样为代码质量发狂的人来说，这种组合极大地减少了我的 <I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">神经紧张</I>，还让我可以得到我想做得最好的东西：编写“防弹”软件。快点行动吧。 </P>
<P>因为这是一个新的系列，所以我非常希望您能一起来推动它前进。如果您对 Groovy 有什么想了解的事情，请 <A href="mailto:aglover@vanwardtechnologies.com" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">发邮件给我</A>，让我知道您的要求！我希望您会继续支持本系列的下一期，在下期中，我将介绍用 Groovy 进行 Ant 脚本编程。 </P><img src ="http://www.blogjava.net/mstar/aggbug/6262.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/mstar/" target="_blank">黑灵</a> 2005-06-17 10:53 <a href="http://www.blogjava.net/mstar/archive/2005/06/17/6262.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>实战 Groovy: 在 Java 应用程序中加一些 Groovy 进来</title><link>http://www.blogjava.net/mstar/archive/2005/06/17/6261.html</link><dc:creator>黑灵</dc:creator><author>黑灵</author><pubDate>Fri, 17 Jun 2005 02:51:00 GMT</pubDate><guid>http://www.blogjava.net/mstar/archive/2005/06/17/6261.html</guid><wfw:comment>http://www.blogjava.net/mstar/comments/6261.html</wfw:comment><comments>http://www.blogjava.net/mstar/archive/2005/06/17/6261.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/mstar/comments/commentRss/6261.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/mstar/services/trackbacks/6261.html</trackback:ping><description><![CDATA[<P align=left><!--StartFragment --> <SPAN class=atitle2>嵌入简单的、易于编写的脚本，从而利用 Groovy 的简单性</SPAN><BR></P>
<P align=left>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR vAlign=top align=left>
<TD>
<P>级别: 中级</P></TD></TR></TBODY></TABLE></P>
<P align=left><A href="http://www-128.ibm.com/developerworks/cn/java/j-pg05245/index.html?ca=dwcn-newsletter-java#author1"><NAME>Andrew Glover</NAME></A><BR>CTO, Vanward Technologies<BR>2005 年 6 月 13 日</P>
<BLOCKQUOTE>
<P align=left><ABSTRACT-EXTENDED>您有没有想过在自己相对复杂的 Java 程序中嵌入 Groovy 简单的、易于编写的脚本呢？在这一期 <I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">实战 Groovy</I> 系列文章中，Andrew Glover 将介绍把 Groovy 集成到 Java 代码中的多种方法，并解释在什么地方、什么时候适合这么做。</ABSTRACT-EXTENDED></P></BLOCKQUOTE>
<P align=left>如果您一直在阅读这个系列，那么您应该已经看到有各种各样使用 Groovy 的有趣方式，Groovy 的主要优势之一就是它的生产力。Groovy 代码通常要比 Java 代码更容易编写，而且编写起来也更快，这使得它有足够的资格成为开发工作包中的一个附件。在另一方面，正如我在这个系列中反复强调的那样，Groovy 并不是 —— 而且也不打算成为 —— Java 语言的替代。所以，这里存在的问题是，能否把 Groovy 集成到 Java 的编程实践中？或者说这样做有什么用？<I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">什么时候</I> 这样做有用？</P>
<P align=left>这个月，我将尝试回答这个问题。我将从一些熟悉的事物开始，即从如何将 Groovy 脚本编译成与 Java 兼容的类文件开始，然后进一步仔细研究 Groovy 的编译工具（<CODE>groovyc</CODE>）是如何让这个奇迹实现的。了解 Groovy 在幕后做了什么是在 Java 代码中使用 Groovy 的第一步。</P>
<P align=left>注意，本月示例中演示的一些编程技术是 <CODE>Groovlets</CODE> 框架和 Groovy 的 <CODE>GroovyTestCase</CODE> 的核心，这些技术我在前面的文章中已经讨论过。 </P>
<P align=left>
<TABLE cellSpacing=0 cellPadding=5 width="30%" align=right border=1>
<TBODY>
<TR>
<TD background=/developerworks/cn/i/bg-gold.gif>
<P><A name=IDAATGX><B>关于本系列</B></A><BR>把任何工具集成到自己的开发实践的关键就是知道什么时候使用它，而什么时候应当把它留在箱子里。脚本语言能够成为工具箱中极为强大的附件，但只在将它恰当应用到合适场景时才这样。为此，<A href="http://www-128.ibm.com/developerworks/cn/views/java/articles.jsp?view_by=search&amp;search_by=%E5%AE%9E%E6%88%98+Groovy%3A" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><I>实战 Groovy</I></A> 的一系列文章专门探索了 Groovy 的实际应用，并告诉您什么时候应用它们，以及如何成功地应用它们。</P></TD></TR></TBODY></TABLE></P>
<P align=left><A name=IDALTGX><SPAN class=atitle2>天作之合？</SPAN></A><BR>在本系列中以前的文章中，当我介绍如何 <A href="http://www.ibm.com/developerworks/java/library/j-pg11094/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">用 Groovy 测试普通 Java 程序</A> 的时候，您可能已经注意到一些奇怪的事：我 <I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">编译了</I> 那些 Groovy 脚本。实际上，我将 groovy 单元测试编译成普通的 Java .class 文件，然后把它们作为 Maven 构建的一部分来运行。</P>
<P align=left>这种编译是通过调用 <CODE>groovyc</CODE> 命令进行的，该命令将 Groovy 脚本编译成普通的 Java 兼容的 .class 文件。例如，如果脚本声明了一个类，那么调用 <CODE>groovyc</CODE> 会生成至少三个 .class 。文件本身会遵守标准的 Java 规则：.class 文件名称要和声明的类名匹配。 </P>
<P align=left>作为示例，请参见清单 1，它创建了一个简单的脚本，脚本声明了几个类。然后，您自己就可以看出 <CODE>groovyc</CODE> 命令生成的结果：</P>
<P align=left><A name=code1><B>清单 1. Groovy 中的类声明和编译</B></A><BR></P>
<P align=left>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
package com.vanward.groovy

class Person {
  fname
  lname
  age
  address
  contactNumbers
  String toString(){
   
    numstr = new StringBuffer()
    if (contactNumbers != null){
     contactNumbers.each{
       numstr.append(it)
       numstr.append(" ")
     }
    }
    "first name: " + fname + " last name: " + lname + 
    " age: " + age + " address: " + address + 
    " contact numbers: " + numstr.toString()
  }
}
class Address {
  street1
  street2
  city
  state
  zip
  String toString(){
   "street1: " + street1 + " street2: " + street2 +
     " city: " + city + " state: " + state + " zip: " + zip
  }
}
class ContactNumber {
  type
  number
  String toString(){
   "Type: " + type + " number: " + number 
  }
}
nums = [new ContactNumber(type:"cell", number:"555.555.9999"), 
	new ContactNumber(type:"office", number:"555.555.5598")]
addr = new Address(street1:"89 Main St.", street2:"Apt #2", 
	city:"Utopia", state:"VA", zip:"34254")
pers = new Person(fname:"Mollie", lname:"Smith", age:34, 
	address:addr, contactNumbers:nums)
println pers.toString()
</CODE></PRE></TD></TR></TBODY></TABLE></P>
<P align=left>在清单 1 中，我声明了三个类 —— <CODE>Person</CODE>、<CODE>Address</CODE> 和 <CODE>ContactNumber</CODE>。之后的代码根据这些新定义的类型创建对象，然后调用 <CODE>toString()</CODE> 方法。迄今为止，Groovy 中的代码还非常简单，但现在来看一下清单 2 中 <CODE>groovyc</CODE> 产生什么样的结果：</P>
<P align=left><A name=code2><B>清单 2. groovyc 命令生成的类</B></A><BR></P>
<P align=left>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
aglover@12d21 /cygdrive/c/dev/project/target/classes/com/vanward/groovy
$ ls -ls  
total 15
 4 -rwxrwxrwx+ 1 aglover  user   3317 May  3 21:12 Address.class
 3 -rwxrwxrwx+ 1 aglover  user   3061 May  3 21:12 BusinessObjects.class
 3 -rwxrwxrwx+ 1 aglover  user   2815 May  3 21:12 ContactNumber.class
 1 -rwxrwxrwx+ 1 aglover  user   1003 May  3 21:12 
   Person$_toString_closure1.class
 4 -rwxrwxrwx+ 1 aglover  user   4055 May  3 21:12 Person.class
</CODE></PRE></TD></TR></TBODY></TABLE></P>
<P align=left>哇！<I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">五个</I> .class 文件！我们了解 <CODE>Person</CODE>、<CODE>Address</CODE> 和 <CODE>ContactNumber</CODE> 文件的意义，但是其他两个文件有什么作用呢？</P>
<P align=left>研究发现，<CODE>Person$_toString_closure1.class</CODE> 是 <CODE>Person</CODE> 类的 <CODE>toString()</CODE> 方法中发现的闭包的结果。它是 <CODE>Person</CODE> 的一个内部类，但是 <CODE>BusinessObjects.class</CODE> 文件是怎么回事 —— 它可能是什么呢？</P>
<P align=left>对 <A href="http://www-128.ibm.com/developerworks/cn/java/j-pg05245/index.html?ca=dwcn-newsletter-java#code1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">清单 1</A> 的深入观察指出：我在脚本主体中编写的代码（声明完三个类之后的代码）变成一个 .class 文件，它的名称采用的是脚本名称。在这个例子中，脚本被命名为 <CODE>BusinessObjects.groovy</CODE>，所以，类定义中没有包含的代码被编译到一个名为 <CODE>BusinessObjects</CODE> 的 .class 文件。</P>
<P align=left><A name=IDAEXGX><SPAN class=atitle3>反编译</SPAN></A><BR>反编译这些类可能会非常有趣。由于 Groovy 处于代码顶层，所以生成的 .java 文件可能相当巨大；不过，您应当注意的是 Groovy 脚本中声明的类（如 <CODE>Person</CODE>） 与类之外的代码（比如 <CODE>BusinessObjects.class</CODE> 中找到的代码）之间的区别。在 Groovy 文件中定义的类完成了 <CODE>GroovyObject</CODE> 的实现，而在类之外定义的代码则被绑定到一个扩展自 <CODE>Script</CODE> 的类。 </P>
<P align=left>例如，如果研究由 BusinessObjects.class 生成的 .java 文件，可以发现：它定义了一个 <CODE>main()</CODE> 方法和一个 <CODE>run()</CODE> 方法。不用惊讶， <CODE>run()</CODE> 方法包含我编写的、用来创建这些对象的新实例的代码，而 <CODE>main()</CODE> 方法则调用 <CODE>run()</CODE> 方法。</P>
<P align=left>这个细节的全部要点再一次回到了：对 Groovy 的理解越好，就越容易把它集成到 Java 程序中。有人也许会问：“为什么我要这么做呢？”好了，我们想说您用 Groovy 开发了一些很酷的东西；那么如果能把这些东西集成到 Java 程序中，那不是很好吗？ </P>
<P align=left>只是为了讨论的原因，我首先<I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">试图</I> 用 Groovy 创建一些有用的东西，然后我再介绍把它嵌入到普通 Java 程序中的各种方法。</P>
<P align=left><A name=IDAZYLX><SPAN class=atitle2>再制作一个音乐 Groovy</SPAN></A><BR>我热爱音乐。实际上，我的 CD 收藏超过了我计算机图书的收藏。多年以来，我把我的音乐截取到不同的计算机上，在这个过程中，我的 MP3 收藏乱到了这样一种层度：只是表示品种丰富的音乐目录就有一大堆。</P>
<P align=left>最近，为了让我的音乐收藏回归有序，我采取了第一步行动。我编写了一个快速的 Groovy 脚本，在某个目录的 MP3 收藏上进行迭代，然后把每个文件的详细信息（例如艺术家、专辑名称等）提供给我。脚本如清单 3 所示：</P>
<P align=left><A name=code3><B>清单 3. 一个非常有用的 Groovy 脚本</B></A><BR></P>
<P align=left>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
package com.vanward.groovy

import org.farng.mp3.MP3File
import groovy.util.AntBuilder

class Song {
  
  mp3file
  Song(String mp3name){   
    mp3file = new MP3File(mp3name)   
  }
  getTitle(){
    mp3file.getID3v1Tag().getTitle()
  }
  getAlbum(){
    mp3file.getID3v1Tag().getAlbum()
  }
  getArtist(){
    mp3file.getID3v1Tag().getArtist()
  }
  String toString(){
    "Artist: " + getArtist() + " Album: " + 
      getAlbum() + " Song: " + getTitle()
  }
  static getSongsForDirectory(sdir){
    println "sdir is: " + sdir
    ant = new AntBuilder()
    scanner = ant.fileScanner {
       fileset(dir:sdir) {
         include(name:"**/*.mp3")
       }
    }
    songs = []
    for(f in scanner){   
      songs &lt;&lt; new Song(f.getAbsolutePath())   
    }
    return songs
  }
}
songs = Song.getSongsForDirectory(args[0])
songs.each{
 println it
}
</CODE></PRE></TD></TR></TBODY></TABLE></P>
<P align=left>正如您所看到的，脚本非常简单，对于像我这样的人来说特别有用。而我要做的全部工作只是把某个具体的目录名传递给它，然后我就会得到该目录中每个 MP3 文件的相关信息（艺术家名称、歌曲名称和专辑） 。</P>
<P align=left>现在让我们来看看，如果要把这个干净的脚本集成到一个能够通过数据库组织音乐甚至播放 MP3 的普通 Java 程序中，我需要做些什么。</P>
<P align=left><A name=IDALZLX><SPAN class=atitle2>Class 文件是类文件</SPAN></A><BR>正如前面讨论过的，我的第一个选项可能只是用 <CODE>groovyc</CODE> 编译脚本。在这个例子中，我期望 <CODE>groovyc</CODE> 创建 <I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">至少</I> 两个 .class 文件 —— 一个用于 <CODE>Song</CODE> 类，另一个用于 <CODE>Song</CODE> 声明之后的脚本代码。</P>
<P align=left>实际上，<CODE>groovyc</CODE> 可能创建 5 个 .class 文件。这是与 <CODE>Songs.groovy</CODE> 包含三个闭包有关，两个闭包在 <CODE>getSongsForDirectory()</CODE> 方法中，另一个在脚本体中，我在脚本体中对 <CODE>Song</CODE> 的集合进行迭代，并调用 <CODE>println</CODE>。</P>
<P align=left>因为 .class 文件中有三个实际上是 Song.class 和 Songs.class 的内部类，所以我只需要把注意力放在两个 .class 文件上。Song.class 直接映射到 Groovy 脚本中的 <CODE>Song</CODE> 声明，并实现了 <CODE>GroovyObject</CODE>，而 Songs.class 则代表我在定义 <CODE>Song</CODE> 之后编写的代码，所以也扩展了 <CODE>Script</CODE>。</P>
<P align=left>此时此刻，关于如何把新编译的 Groovy 代码集成到 Java 代码，我有两个选择：可以通过 Songs.class 文件中的 <CODE>main()</CODE> 方法运行代码 （因为它扩展了 <CODE>Script</CODE>），或者可以将 Song.class 包含到类路径中，就像在 Java 代码中使用其他对象一样使用它。 </P>
<P align=left><A name=IDAW1LX><SPAN class=atitle2>变得更容易些</SPAN></A><BR>通过 <CODE>java</CODE> 命令调用 Songs.class 文件非常简单，只要您记得把 Groovy 相关的依赖关系和 Groovy 脚本需要的依赖关系包含进来就可以。把 Groovy 需要的类全都包含进来的最简单方法就是把包含全部内容的 Groovy 可嵌入 jar 文件添加到类路径中。在我的例子中，这个文件是 groovy-all-1.0-beta-10.jar。要运行 Songs.class，需要记得包含将要用到的 MP3 库（jid3lib-0.5.jar&gt;），而且因为我使用 <CODE>AntBuilder</CODE>，所以我还需要在类路径中包含 <CODE>Ant</CODE>。清单 4 把这些放在了一起：</P>
<P align=left><A name=code4><B>清单 4. 通过 Java 命令行调用 Groovy</B></A><BR></P>
<P align=left>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
c:\dev\projects&gt;java -cp  ./target/classes/;c:/dev/tools/groovy/
  groovy-all-1.0-beta-10.jar;C:/dev/tools/groovy/ant-1.6.2.jar;
  C:/dev/projects-2.0/jid3lib-0.5.jar  
  com.vanward.groovy.Songs c:\dev09\music\mp3s
Artist: U2 Album: Zooropa Song: Babyface
Artist: James Taylor Album: Greatest Hits Song: Carolina in My Mind
Artist: James Taylor Album: Greatest Hits Song: Fire and Rain
Artist: U2 Album: Zooropa Song: Lemon
Artist: James Taylor Album: Greatest Hits Song: Country Road
Artist: James Taylor Album: Greatest Hits Song: Don't Let Me 
  Be Lonely Tonight
Artist: U2 Album: Zooropa Song: Some Days Are Better Than Others
Artist: Paul Simon Album: Graceland Song: Under African Skies
Artist: Paul Simon Album: Graceland Song: Homeless
Artist: U2 Album: Zooropa Song: Dirty Day
Artist: Paul Simon Album: Graceland Song: That Was Your Mother
</CODE></PRE></TD></TR></TBODY></TABLE></P>
<P align=left><A name=IDAS2LX><SPAN class=atitle2>把 Groovy 嵌入 Java 代码</SPAN></A><BR>虽然命令行的解决方案简单有趣，但它并不是所有问题的最终解决方案。如果对更高层次的完善感兴趣，那么可能将 MP3 歌曲工具直接导入 Java 程序。在这个例子中，我想导入 Song.class ，并像在 Java 语言中使用其他类那样使用它。类路径的问题与上面相同 ：我需要确保包含了 <I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">uber-Groovy</I> jar 文件、<CODE>Ant</CODE> 和 jid3lib-0.5.jar 文件。在清单 5 中，可以看到如何将 Groovy MP3 工具导入简单的 Java 类中：</P>
<P align=left><A name=code5><B>清单 5. 嵌入的 Groovy 代码</B></A><BR></P>
<P align=left>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
package com.vanward.gembed;

import com.vanward.groovy.Song;
import java.util.Collection;
import java.util.Iterator;

public class SongEmbedGroovy{

 public static void main(String args[]) {
  Collection coll = (Collection)Song.getSongsForDirectory
    ("C:\\music\\temp\\mp3s");
  for(Iterator it = coll.iterator(); it.hasNext();){
    System.out.println(it.next());
  }	
 }
}
</CODE></PRE></TD></TR></TBODY></TABLE></P>
<P align=left><A name=IDAI3LX><SPAN class=atitle2>Groovy 类加载器</SPAN></A><BR>就在您以为自己已经掌握全部的时候，我要告诉您的是，还有更多在 Java 语言中使用 Groovy 的方法。除了通过直接编译把 Groovy 脚本集成到 Java 程序中的这个选择之外，当我想直接嵌入脚本时，还有其他一些选择。</P>
<P align=left>例如，我可以用 Groovy 的 <CODE>GroovyClassLoader</CODE> ，<I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">动态地</I> 加载一个脚本并执行它的行为，如清单 6 所示：</P>
<P align=left><A name=code6><B>清单 6. GroovyClassLoader 动态地加载并执行 Groovy 脚本</B></A><BR></P>
<P align=left>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
package com.vanward.gembed;

import groovy.lang.GroovyClassLoader;
import groovy.lang.GroovyObject;
import groovy.lang.MetaMethod;
import java.io.File;

public class CLEmbedGroovy{

 public static void main(String args[]) throws Throwable{
 
  ClassLoader parent = CLEmbedGroovy.class.getClassLoader();
  GroovyClassLoader loader = new GroovyClassLoader(parent);
  
  Class groovyClass = loader.parseClass(
    new File("C:\\dev\\groovy-embed\\src\\groovy\\
      com\\vanward\\groovy\\Songs.groovy"));
  
  GroovyObject groovyObject = (GroovyObject) 
    groovyClass.newInstance();
  
  Object[] path = {"C:\\music\\temp\\mp3s"};
  groovyObject.setProperty("args", path);
  Object[] argz = {};
  
  groovyObject.invokeMethod("run", argz);
 
 }
}
</CODE></PRE></TD></TR></TBODY></TABLE></P>
<P align=left>
<TABLE cellSpacing=0 cellPadding=5 width="30%" align=right border=1>
<TBODY>
<TR>
<TD background=/developerworks/cn/i/bg-gold.gif>
<P><A name=IDAA4LX><B>Meta，宝贝</B></A><BR>如果您属于那群疯狂的人中的一员，热爱反射，喜欢利用它们能做的精彩事情，那么您将热衷于 Groovy 的 <CODE>Meta</CODE> 类。就像反射一样，使用这些类，您可以发现 <CODE>GroovyObject</CODE> 的各个方面（例如它的方法），这样就可以实际地 <I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">创建</I> 新的行为并执行它。而且，这是 Groovy 的核心 —— 想想运行脚本时它将如何发威吧！</P></TD></TR></TBODY></TABLE></P>
<P align=left>注意，默认情况下，类加载器将加载与脚本名称对应的类 —— 在这个例子中是 Songs.class，而<I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">不是</I> Song.class&gt;。因为我（和您）知道 Songs.class 扩展了 Groovy 的 Script 类，所以不用想也知道接下来要做的就是执行 <CODE>run()</CODE> 方法。</P>
<P align=left>您记起，我的 Groovy 脚本也依赖于运行时参数。所以，我需要恰当地配置 <CODE>args</CODE> 变量，在这个例子中，我把第一个元素设置为目录名。</P>
<P align=left><A name=IDA54LX><SPAN class=atitle2>更加动态的选择</SPAN></A><BR>对于使用编译好的类，而且，通过类加载器来动态加载 <CODE>GroovyObject</CODE> 的替代，是使用 Groovy 优美的 <CODE>GroovyScriptEngine</CODE> 和 <CODE>GroovyShell</CODE> 动态地执行 Groovy 脚本。 </P>
<P align=left>把 <CODE>GroovyShell</CODE> 对象嵌入普通 Java 类，可以像类加载器所做的那样动态执行 Groovy 脚本。除此之外，它还提供了大量关于控制脚本运行的选项。在清单 7 中，可以看到 <CODE>GroovyShell</CODE> 嵌入到普通 Java 类的方式：</P>
<P align=left><A name=code7><B>清单 7. 嵌入 GroovyShell</B></A><BR></P>
<P align=left>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
package com.vanward.gembed;

import java.io.File;
import groovy.lang.GroovyShell;

public class ShellRunEmbedGroovy{

 public static void main(String args[]) throws Throwable{
						
  String[] path = {"C:\\music\\temp\\mp3s"};		
  GroovyShell shell = new GroovyShell();
  shell.run(new File("C:\\dev\\groovy-embed\\src\\groovy\\
    com\\vanward\\groovy\\Songs.groovy"), 
    path);
 }
}
</CODE></PRE></TD></TR></TBODY></TABLE></P>
<P align=left>可以看到，运行 Groovy 脚本非常容易。我只是创建了 <CODE>GroovyShell</CODE> 的实例，传递进脚本名称，然后调用 <CODE>run()</CODE> 方法。</P>
<P align=left>还可以做其他事情。如果您喜欢，那么也可以得到自己脚本的 <CODE>Script</CODE> 类型的 <CODE>GroovyShell</CODE> 实例。使用 <CODE>Script</CODE> 类型，您就可以传递进一个 <CODE>Binding</CODE> 对象，其中包含任何运行时值，然后再继续调用 <CODE>run()</CODE> 方法，如清单 8 所示：</P>
<P align=left><A name=code8><B>清单 8. 有趣的 GroovyShell</B></A><BR></P>
<P align=left>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
package com.vanward.gembed;

import java.io.File;
import groovy.lang.Binding;
import groovy.lang.GroovyShell;
import groovy.lang.Script;

public class ShellParseEmbedGroovy{

 public static void main(String args[]) throws Throwable{
  GroovyShell shell = new GroovyShell();
  Script scrpt = shell.parse(
    new File("C:\\dev\\groovy-embed\\src\\groovy\\
      com\\vanward\\groovy\\Songs.groovy"));
  
  Binding binding = new Binding();
  Object[] path = {"C:\\music\\temp\\mp3s"};
  binding.setVariable("args",path);			
  scrpt.setBinding(binding);
  
  scrpt.run();
 }
}
</CODE></PRE></TD></TR></TBODY></TABLE></P>
<P align=left><A name=IDARBMX><SPAN class=atitle2>Groovy 的脚本引擎</SPAN></A><BR><CODE>GroovyScriptEngine</CODE> 对象动态运行脚本的时候，非常像 <CODE>GroovyShell</CODE>。区别在于：对于 <CODE>GroovyScriptEngine</CODE>，您可以在实例化的时候给它提供一系列目录，然后让它根据要求去执行多个脚本，如清单 9 所示：</P>
<P align=left><A name=code9><B>清单 9. GroovyScriptEngine 的作用</B></A><BR></P>
<P align=left>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
package com.vanward.gembed;

import java.io.File;
import groovy.lang.Binding;
import groovy.util.GroovyScriptEngine;

public class ScriptEngineEmbedGroovy{

 public static void main(String args[]) throws Throwable{
			
  String[] paths = {"C:\\dev\\groovy-embed\\src\\groovy\\
    com\\vanward\\groovy"};
  GroovyScriptEngine gse = new GroovyScriptEngine(paths);
  Binding binding = new Binding();
  Object[] path = {"C:\\music\\temp\\mp3s"};
  binding.setVariable("args",path);
  
  gse.run("Songs.groovy", binding);
  gse.run("BusinessObjects.groovy", binding);
 }
}
</CODE></PRE></TD></TR></TBODY></TABLE></P>
<P align=left>在清单 9 中，我向实例化的 <CODE>GroovyScriptEngine</CODE> 传入了一个数组，数据中包含我要处理的路径，然后创建大家熟悉的 <CODE>Binding</CODE> 对象，然后再执行仍然很熟悉的 <CODE>Songs.groovy</CODE> 脚本。只是为了好玩，我还执行了<CODE>BusinessObjects.groovy</CODE> 脚本，您或许还能回忆起来，它在开始这次讨论的时候出现过。</P>
<P align=left><A name=IDA4CMX><SPAN class=atitle2>Bean 脚本框架</SPAN></A><BR>最后，当然并不是最不重要的，是来自 Jakarta 的古老的 Bean 脚本框架（ Bean Scripting Framework —— BSF）。BSF 试图提供一个公共的 API，用来在普通 Java 应用程序中嵌入各种脚本语言（包括 Groovy）。这个标准的、但是有争议的最小公因子方法，可以让您毫不费力地将 Java 应用程序嵌入 Groovy 脚本。</P>
<P align=left>还记得前面的 <CODE>BusinessObjects</CODE> 脚本吗？在清单 10 中，可以看到 BSF 可以多么容易地让我把这个脚本插入普通 Java 程序中：</P>
<P align=left><A name=code10><B>清单 10. BSF 开始工作了</B></A><BR></P>
<P align=left>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
package com.vanward.gembed;

import org.apache.bsf.BSFManager;
import org.codehaus.groovy.runtime.DefaultGroovyMethods;
import java.io.File;
import groovy.lang.Binding;

public class BSFEmbedGroovy{

 public static void main(String args[]) throws Exception {
  String fileName = "C:\\dev\\project\\src\\groovy\\
    com\\vanward\\groovy\\BusinessObjects.groovy";
  //this is required for bsf-2.3.0
  //the "groovy" and "gy" are extensions
  BSFManager.registerScriptingEngine("groovy", 
    "org.codehaus.groovy.bsf.GroovyEngine", new 
      String[] { "groovy" });
  BSFManager manager = new BSFManager();
  //DefaultGroovyMethods.getText just returns a 
  //string representation of the contents of the file
  manager.exec("groovy", fileName, 0, 0, 
    DefaultGroovyMethods.getText(new File(fileName)));
  }
}
</CODE></PRE></TD></TR></TBODY></TABLE></P>
<P align=left><A name=IDATDMX><SPAN class=atitle2>结束语</SPAN></A><BR>如果在本文中有一件事是清楚的话，那么只件事就是 Groovy 为了 Java 代码内部的重用提供了一堆选择。从把 Groovy 脚本编译成普通 Java .class 文件，到动态地加载和运行脚本，这些问题需要考虑的一些关键方面是灵活性和耦合。把 Groovy 脚本编译成普通 .class 文件是 <I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">使用</I> 您打算嵌入的功能的最简单选择，但是动态加载脚本可以使添加或修改脚本的行为变得更容易，同时还不必在编译上牺牲时间。（当然，这个选项只是在接口不变的情况下才有用。）</P>
<P align=left>把脚本语言嵌入普通 Java 不是每天都发生，但是机会确实不断出现。这里提供的示例把一个简单的目录搜索工具嵌入到基于 Java 的应用程序中，这样 Java 应用程序就可以很容易地变成 MP3 播放程序或者其他 MP3 播放工具。虽然我 <I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">可以</I> 用 Java 代码重新编写 MP3 文件搜索器，但是我不需要这么做：Groovy 极好地兼容 Java 语言，而且，我很有兴趣去摆弄所有选项！</P><img src ="http://www.blogjava.net/mstar/aggbug/6261.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/mstar/" target="_blank">黑灵</a> 2005-06-17 10:51 <a href="http://www.blogjava.net/mstar/archive/2005/06/17/6261.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Practically Groovy: Stir some Groovy into your Java apps</title><link>http://www.blogjava.net/mstar/archive/2005/06/08/5724.html</link><dc:creator>黑灵</dc:creator><author>黑灵</author><pubDate>Wed, 08 Jun 2005 04:36:00 GMT</pubDate><guid>http://www.blogjava.net/mstar/archive/2005/06/08/5724.html</guid><wfw:comment>http://www.blogjava.net/mstar/comments/5724.html</wfw:comment><comments>http://www.blogjava.net/mstar/archive/2005/06/08/5724.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/mstar/comments/commentRss/5724.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/mstar/services/trackbacks/5724.html</trackback:ping><description><![CDATA[<!--StartFragment -->&nbsp;<SPAN class=atitle2>Exploit Groovy's simplicity by embedding simple, easy-to-write scripts</SPAN><BR>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR vAlign=top align=left>
<TD>
<P>Level: Intermediate</P></TD></TR></TBODY></TABLE>
<P><A href="http://www-128.ibm.com/developerworks/library/j-pg05245/#author1"><NAME>Andrew Glover</NAME></A> (<A href="mailto:aglover@vanwardtechnologies.com?subject=Stir%20some%20Groovy%20into%20your%20Java%20apps">aglover@vanwardtechnologies.com</A>)<BR>CTO, Vanward Technologies<BR>24 May 2005</P>
<BLOCKQUOTE><IMG height=38 alt="Column icon" src="http://www-128.ibm.com/developerworks/i/c-j-groovy.jpg" width=38 align=left border=0><ABSTRACT-EXTENDED>Ever thought about embedding Groovy's simple, easy-to-write scripts in your more complex Java™ programs? In this installment of <I>Practically Groovy</I>, Andrew Glover shows you the many ways to incorporate Groovy into your Java code and explains where and when it's appropriate to do so.</ABSTRACT-EXTENDED></BLOCKQUOTE>
<P>If you've been reading this series for a while, you've seen that there are all sorts of interesting ways to use Groovy, and that one of the chief advantages of Groovy is its productivity. Groovy code is often easier and faster to write than Java code, which makes it a worthwhile addition to your development toolkit. On the other hand, as I have stressed over and over again in this series, Groovy isn't -- and isn't intended to be -- a replacement for the Java language. So the question is, can you incorporate Groovy into your Java programming practice, and is it, or <I>when is it</I>, useful to do so?</P>
<P>This month, I'll try to answer that question. I'll start with something familiar -- how Groovy scripts get compiled into Java-compatible class files -- and then delve further under the hood to see exactly how the Groovy compilation facility (<CODE>groovyc</CODE>) makes this magic happen. Understanding what's going on under the hood with Groovy is the first step to using it in your Java code</P>
<P>Note that some of the programming techniques demonstrated in this month's examples are at the core of the <CODE>Groovlets</CODE> framework and Groovy's <CODE>GroovyTestCase</CODE>, which I've discussed in previous articles. </P>
<P>
<TABLE cellSpacing=0 cellPadding=5 width="30%" align=right border=1>
<TBODY>
<TR>
<TD background=/developerworks/i/bg-gold.gif>
<P><A name=N1006F><B>About this series</B></A><BR>The key to incorporating any tool into your development practice is knowing when to use it and when to leave it in the box. Scripting languages can be an extremely powerful addition to your toolkit, but only when applied properly to appropriate scenarios. To that end, <A href="http://www.ibm.com/developerworks/views/java/articles.jsp?sort_order=desc&amp;expand=&amp;sort_by=Date&amp;show_abstract=true&amp;view_by=Search&amp;search_by=practically%20groovy"><I>Practically Groovy</I></A> is a series of articles dedicated to exploring the practical uses of Groovy, and teaching you when and how to apply them successfully. </P></TD></TR></TBODY></TABLE></P>
<P><A name=N1007E><SPAN class=atitle2>A marriage made in heaven?</SPAN></A><BR>Earlier in this series, when I showed you how to <A href="http://www.ibm.com/developerworks/java/library/j-pg11094/">unit test normal Java programs with Groovy</A>, you may have noticed something peculiar: I <I>compiled</I> those Groovy scripts. In fact, I compiled my groovy unit tests into normal Java .class files and ran them as part of a Maven build. </P>
<P>This type of compilation is done by invoking the <CODE>groovyc</CODE> command, which compiles Groovy scripts into plain old Java-compatible .class files. For example, if your script declares three classes, calling <CODE>groovyc</CODE> will result in at least three .class files. The files themselves will follow the standard Java rules where .class file names match declared class names. </P>
<P>As an example, take a look at Listing 1, which creates a simple script that declares a few classes. You can then see for yourself what the <CODE>groovyc</CODE> command generates: </P><A name=code1><B>Listing 1. Class declaration and compilation in Groovy</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
package com.vanward.groovy

class Person {
  fname
  lname
  age
  address
  contactNumbers
  String toString(){
   
    numstr = new StringBuffer()
    if (contactNumbers != null){
     contactNumbers.each{
       numstr.append(it)
       numstr.append(" ")
     }
    }
    "first name: " + fname + " last name: " + lname + 
    " age: " + age + " address: " + address + 
    " contact numbers: " + numstr.toString()
  }
}
class Address {
  street1
  street2
  city
  state
  zip
  String toString(){
   "street1: " + street1 + " street2: " + street2 +
     " city: " + city + " state: " + state + " zip: " + zip
  }
}
class ContactNumber {
  type
  number
  String toString(){
   "Type: " + type + " number: " + number 
  }
}
nums = [new ContactNumber(type:"cell", number:"555.555.9999"), 
	new ContactNumber(type:"office", number:"555.555.5598")]
addr = new Address(street1:"89 Main St.", street2:"Apt #2", 
	city:"Utopia", state:"VA", zip:"34254")
pers = new Person(fname:"Mollie", lname:"Smith", age:34, 
	address:addr, contactNumbers:nums)
println pers.toString()
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>In Listing 1, I declared three classes -- <CODE>Person</CODE>, <CODE>Address</CODE>, and <CODE>ContactNumber</CODE>. The code that follows creates objects of the newly defined types and calls a <CODE>toString()</CODE> method. Pretty simple stuff so far, but now look at what <CODE>groovyc</CODE> generated as a result, in Listing 2:</P><A name=code2><B>Listing 2. Classes generated by the groovyc command</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
aglover@12d21 /cygdrive/c/dev/project/target/classes/com/vanward/groovy
$ ls -ls  
total 15
 4 -rwxrwxrwx+ 1 aglover  user   3317 May  3 21:12 Address.class
 3 -rwxrwxrwx+ 1 aglover  user   3061 May  3 21:12 BusinessObjects.class
 3 -rwxrwxrwx+ 1 aglover  user   2815 May  3 21:12 ContactNumber.class
 1 -rwxrwxrwx+ 1 aglover  user   1003 May  3 21:12 
   Person$_toString_closure1.class
 4 -rwxrwxrwx+ 1 aglover  user   4055 May  3 21:12 Person.class
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>Wow, <I>five</I> .class files! The <CODE>Person</CODE>, <CODE>Address</CODE>, and <CODE>ContactNumber</CODE> files make sense, but what about the other two?</P>
<P>It turns out that the <CODE>Person$_toString_closure1.class</CODE> is the result of the closure found in the <CODE>Person</CODE> class's <CODE>toString()</CODE> method. It is internally an inner class of <CODE>Person</CODE>. But what about the <CODE>BusinessObjects.class</CODE> file -- what could it be? </P>
<P>A closer look at <A href="http://www-128.ibm.com/developerworks/library/j-pg05245/#code1">Listing 1</A> reveals that the code I wrote in the main body of the script, after I declared those three classes, became a .class file, which is named after the name of the script. In this case, the script was named <CODE>BusinessObjects.groovy</CODE>, so the code not contained in a class definition was then compiled into a .class file named <CODE>BusinessObjects</CODE>. </P>
<P><A name=N10105><SPAN class=atitle3>Coding in reverse</SPAN></A><BR>Decompiling these classes can be quite interesting. The resultant .java files can be rather large because of the nature of Groovy's under-the-covers code; however, what you will notice is the difference between classes declared in a Groovy script (like <CODE>Person</CODE>) and the code that lives outside of classes (like that found in <CODE>BusinessObjects.class</CODE>). Classes defined in Groovy files end up implementing <CODE>GroovyObject</CODE> and code defined outside of a class is bundled into a class that extends <CODE>Script</CODE>. </P>
<P>For example, if you examine the resultant .java file of BusinessObjects.class, you'll find it defines a <CODE>main()</CODE> method and a <CODE>run()</CODE> method. Not surprisingly, the <CODE>run()</CODE> method contains the code I wrote to create new instances of those objects and the <CODE>main()</CODE> method calls the <CODE>run()</CODE> method. </P>
<P>Again, the point of all this detail is that the better you understand Groovy, the easier it will be for you to incorporate it into your Java programs. "And why would I want to do that," you ask? Well, let's just say you developed something cool in Groovy; wouldn't it be nice to be able to incorporate it into your Java programs, too? </P>
<P>Just for the sake of argument, I'll first <I>attempt to</I> create something useful in Groovy; then, I can explore the various ways it could be embedded in a normal Java program. </P>
<P><A name=N1013E><SPAN class=atitle2>Making music Groovy again</SPAN></A><BR>I love music. In fact, my CD collection rivals my computer books collection. Over the years, I've ripped my music onto various computers and, in the process, muddled my MP3 collection to the point where I have numerous directories representing a cornucopia of music.</P>
<P>Recently, I took a first step toward getting my music collection back in order. I wrote a quick Groovy script to iterate over a collection of MP3 files in a directory and give me detailed information about each file, such as artist, album title, etc. The script is shown in Listing 3: </P><A name=code3><B>Listing 3. A very useful Groovy script</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
package com.vanward.groovy

import org.farng.mp3.MP3File
import groovy.util.AntBuilder

class Song {
  
  mp3file
  Song(String mp3name){   
    mp3file = new MP3File(mp3name)   
  }
  getTitle(){
    mp3file.getID3v1Tag().getTitle()
  }
  getAlbum(){
    mp3file.getID3v1Tag().getAlbum()
  }
  getArtist(){
    mp3file.getID3v1Tag().getArtist()
  }
  String toString(){
    "Artist: " + getArtist() + " Album: " + 
      getAlbum() + " Song: " + getTitle()
  }
  static getSongsForDirectory(sdir){
    println "sdir is: " + sdir
    ant = new AntBuilder()
    scanner = ant.fileScanner {
       fileset(dir:sdir) {
         include(name:"**/*.mp3")
       }
    }
    songs = []
    for(f in scanner){   
      songs &lt;&lt; new Song(f.getAbsolutePath())   
    }
    return songs
  }
}
songs = Song.getSongsForDirectory(args[0])
songs.each{
 println it
}
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>As you can see, the script is fairly simple, especially for being so useful to someone in my shoes. All I have to do is pass in a particular directory name and I'm given the relevant information (artist name, song name, and album) for every MP3 file in the directory.</P>
<P>Now let's see what I need to do to incorporate this nifty script into a normal Java program that could organize music via a database or even play MP3s. </P>
<P><A name=N1015B><SPAN class=atitle2>Class files are class files</SPAN></A><BR>As discussed earlier, my first option would be to simply compile the script using <CODE>groovyc</CODE>. In this case, I expect <CODE>groovyc</CODE> would create <I>at least</I> two .class files -- one for my <CODE>Song</CODE> class and another for the script code following <CODE>Song</CODE>'s declaration. </P>
<P>In fact, <CODE>groovyc</CODE> would generate five .class files. This correlates to the fact that <CODE>Songs.groovy</CODE> contains three closures, two in the <CODE>getSongsForDirectory()</CODE> method and one in the script body, where I iterated over the collection of <CODE>Song</CODE>s and called <CODE>println</CODE>. </P>
<P>Because three of the .class files are really inner classes of Song.class and Songs.class, I only need to focus on two .class files. Song.class maps directly to the <CODE>Song</CODE> declaration in the Groovy script and implements <CODE>GroovyObject</CODE>, while Songs.class represents the script code after I defined <CODE>Song</CODE>, and therefore extends <CODE>Script</CODE>. </P>
<P>At this point, I have two options for how I will incorporate my newly compiled Groovy code into Java code: I could actually run the code via the <CODE>main()</CODE> method found in the Songs.class file (because it extends <CODE>Script</CODE>), or I could include the Song.class in my classpath and use it as I would any other object in my Java code. </P>
<P><A name=N101AC><SPAN class=atitle2>Take it easier</SPAN></A><BR>Calling the Songs.class file via the <CODE>java</CODE> command is painfully simple, as long as you remember to include Groovy's associated dependencies and any dependencies your Groovy script may have required. The easiest way to include Groovy's required classes is to insert the all-in-one Groovy-embeddable jar file in your classpath. In my case, the file is groovy-all-1.0-beta-10.jar. To run Songs.class, I'll also need to remember to include the MP3 library I used (jid3lib-0.5.jar&gt;), and because I use the <CODE>AntBuilder</CODE>, I will need to include <CODE>Ant</CODE> in my classpath as well. Listing 4 puts it together:</P><A name=code4><B>Listing 4. Groovy via the Java command line</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
c:\dev\projects&gt;java -cp  ./target/classes/;c:/dev/tools/groovy/
  groovy-all-1.0-beta-10.jar;C:/dev/tools/groovy/ant-1.6.2.jar;
  C:/dev/projects-2.0/jid3lib-0.5.jar  
  com.vanward.groovy.Songs c:\dev09\music\mp3s
Artist: U2 Album: Zooropa Song: Babyface
Artist: James Taylor Album: Greatest Hits Song: Carolina in My Mind
Artist: James Taylor Album: Greatest Hits Song: Fire and Rain
Artist: U2 Album: Zooropa Song: Lemon
Artist: James Taylor Album: Greatest Hits Song: Country Road
Artist: James Taylor Album: Greatest Hits Song: Don't Let Me 
  Be Lonely Tonight
Artist: U2 Album: Zooropa Song: Some Days Are Better Than Others
Artist: Paul Simon Album: Graceland Song: Under African Skies
Artist: Paul Simon Album: Graceland Song: Homeless
Artist: U2 Album: Zooropa Song: Dirty Day
Artist: Paul Simon Album: Graceland Song: That Was Your Mother
</CODE></PRE></TD></TR></TBODY></TABLE>
<P><A name=N101CC><SPAN class=atitle2>Embedding Groovy in Java code</SPAN></A><BR>While the command-line solution is easy and fun, it's not the be all, end all solution. If I were interested in a higher level of sophistication, I could import my MP3 song utility directly into my Java program. In this case, I'd import Song.class and use it as I would use any other class in the Java language. The classpath issues are the same as above: I need to make sure I include the <I>uber-Groovy</I> jar file, <CODE>Ant</CODE>, and the jid3lib-0.5.jar file. In Listing 5, you can see how I import my Groovy MP3 utility into a simple Java class:</P><A name=code5><B>Listing 5. Embedded Groovy code</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
package com.vanward.gembed;

import com.vanward.groovy.Song;
import java.util.Collection;
import java.util.Iterator;

public class SongEmbedGroovy{

 public static void main(String args[]) {
  Collection coll = (Collection)Song.getSongsForDirectory
    ("C:\\music\\temp\\mp3s");
  for(Iterator it = coll.iterator(); it.hasNext();){
    System.out.println(it.next());
  }	
 }
}
</CODE></PRE></TD></TR></TBODY></TABLE>
<P><A name=N101E7><SPAN class=atitle2>Groovy classloaders</SPAN></A><BR>Just when you thought you'd learned it all, it turns out there are a few more ways to play with Groovy in the Java language. In addition to having options for how I will incorporate Groovy scripts into my Java programs through direct compilation, I also have some choices when it comes to how I will embed the scripts themselves.</P>
<P>For example, I could use Groovy's <CODE>GroovyClassLoader</CODE> to <I>dynamically</I> load a Groovy script and execute its behavior, as shown in Listing 6:</P><A name=code6><B>Listing 6. GroovyClassLoader dynamically loads and executes a Groovy script</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
package com.vanward.gembed;

import groovy.lang.GroovyClassLoader;
import groovy.lang.GroovyObject;
import groovy.lang.MetaMethod;
import java.io.File;

public class CLEmbedGroovy{

 public static void main(String args[]) throws Throwable{
 
  ClassLoader parent = CLEmbedGroovy.class.getClassLoader();
  GroovyClassLoader loader = new GroovyClassLoader(parent);
  
  Class groovyClass = loader.parseClass(
    new File("C:\\dev\\groovy-embed\\src\\groovy\\
      com\\vanward\\groovy\\Songs.groovy"));
  
  GroovyObject groovyObject = (GroovyObject) 
    groovyClass.newInstance();
  
  Object[] path = {"C:\\music\\temp\\mp3s"};
  groovyObject.setProperty("args", path);
  Object[] argz = {};
  
  groovyObject.invokeMethod("run", argz);
 
 }
}
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>
<TABLE cellSpacing=0 cellPadding=5 width="30%" align=right border=1>
<TBODY>
<TR>
<TD background=/developerworks/i/bg-gold.gif>
<P><A name=N10207><B>Meta me, baby</B></A><BR>If you are one of those crazy cats who love reflection and the wonderful things you can do with it, then you'll go nuts over Groovy's <CODE>Meta</CODE>classes. Just like with reflection, using these classes, you can discover aspects about a <CODE>GroovyObject</CODE>, like its methods, and you can actually <I>create</I> new behavior and execute it. This, by the way, is the heart of Groovy -- and just imagine how it works when you're running scripts! </P></TD></TR></TBODY></TABLE></P>
<P>Note that by default, the class loader loads the class corresponding to the script name -- in this case Songs.class, <I>not</I> Song.class&gt;. Because I (and you) know that Songs.class extends Groovy's Script class, it's a no-brainer that my next move is to execute the <CODE>run()</CODE> method. </P>
<P>You will recall that my Groovy script was also dependent on run-time arguments. So, I need to configure the <CODE>args</CODE> variable appropriately, as in this case I set the first element to a directory name. </P>
<P><A name=N1022D><SPAN class=atitle2>More dynamic options</SPAN></A><BR>An alternative to using compiled classes and dynamically loading <CODE>GroovyObject</CODE>s via classloaders is to use Groovy's nifty <CODE>GroovyScriptEngine</CODE> and the <CODE>GroovyShell</CODE> to dynamically execute Groovy scripts. </P>
<P>Embedding the <CODE>GroovyShell</CODE> object in your normal Java classes lets you dynamically execute Groovy scripts just as the classloader does. What's more, it gives you a number of options for how you will run your scripts. In Listing 7, you can see how <CODE>GroovyShell</CODE> is embedded in a normal Java class:</P><A name=code7><B>Listing 7. Embedding the GroovyShell</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
package com.vanward.gembed;

import java.io.File;
import groovy.lang.GroovyShell;

public class ShellRunEmbedGroovy{

 public static void main(String args[]) throws Throwable{
						
  String[] path = {"C:\\music\\temp\\mp3s"};		
  GroovyShell shell = new GroovyShell();
  shell.run(new File("C:\\dev\\groovy-embed\\src\\groovy\\
    com\\vanward\\groovy\\Songs.groovy"), 
    path);
 }
}
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>As you can see, running a Groovy script is quite easy. I simply create an instance of <CODE>GroovyShell</CODE>, pass in the script name, and call the <CODE>run()</CODE> method. </P>
<P>But there's more. If you are so inclined, you can also ask a <CODE>GroovyShell</CODE> instance for the <CODE>Script</CODE> type of your script. With the <CODE>Script</CODE> type, you could then pass in a <CODE>Binding</CODE> object containing any run-time values and then proceed to call the <CODE>run()</CODE> method, as shown in Listing 8: </P><A name=code8><B>Listing 8. Fun with the GroovyShell</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
package com.vanward.gembed;

import java.io.File;
import groovy.lang.Binding;
import groovy.lang.GroovyShell;
import groovy.lang.Script;

public class ShellParseEmbedGroovy{

 public static void main(String args[]) throws Throwable{
  GroovyShell shell = new GroovyShell();
  Script scrpt = shell.parse(
    new File("C:\\dev\\groovy-embed\\src\\groovy\\
      com\\vanward\\groovy\\Songs.groovy"));
  
  Binding binding = new Binding();
  Object[] path = {"C:\\music\\temp\\mp3s"};
  binding.setVariable("args",path);			
  scrpt.setBinding(binding);
  
  scrpt.run();
 }
}
</CODE></PRE></TD></TR></TBODY></TABLE>
<P><A name=N10285><SPAN class=atitle2>Groovy's script engine</SPAN></A><BR>The <CODE>GroovyScriptEngine</CODE> object works much like the <CODE>GroovyShell</CODE> for dynamically running scripts. What's different about <CODE>GroovyScriptEngine</CODE> is that you can give it a series of directories upon instantiation and then have it evaluate multiple scripts on demand, as shown in Listing 9: </P><A name=code9><B>Listing 9. The GroovyScriptEngine in action</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
package com.vanward.gembed;

import java.io.File;
import groovy.lang.Binding;
import groovy.util.GroovyScriptEngine;

public class ScriptEngineEmbedGroovy{

 public static void main(String args[]) throws Throwable{
			
  String[] paths = {"C:\\dev\\groovy-embed\\src\\groovy\\
    com\\vanward\\groovy"};
  GroovyScriptEngine gse = new GroovyScriptEngine(paths);
  Binding binding = new Binding();
  Object[] path = {"C:\\music\\temp\\mp3s"};
  binding.setVariable("args",path);
  
  gse.run("Songs.groovy", binding);
  gse.run("BusinessObjects.groovy", binding);
 }
}
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>In Listing 9, I pass an array containing my desired path into my instantiated <CODE>GroovyScriptEngine</CODE>, create the old familiar <CODE>Binding</CODE> object, and then execute the also familiar <CODE>Songs.groovy</CODE> script. Just for fun, I also execute the <CODE>BusinessObjects.groovy</CODE> script, which you may recall from the beginning of this discussion.</P>
<P><A name=N102B8><SPAN class=atitle2>The Bean Scripting Framework</SPAN></A><BR>Last, but certainly not least, is the venerable Bean Scripting Framework (BSF) from Jakarta. BSF attempts to offer a common API for embedding any scripting language in a normal Java application, including Groovy. This standard, but arguably least-common-denominator approach, allows you to embed a Groovy script effortlessly. </P>
<P>Remember the <CODE>BusinessObjects</CODE> script from earlier? In Listing 10, you can see how easily BSF lets me plug it into a normal Java program:</P><A name=code10><B>Listing 10. BSF goes to work</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
package com.vanward.gembed;

import org.apache.bsf.BSFManager;
import org.codehaus.groovy.runtime.DefaultGroovyMethods;
import java.io.File;
import groovy.lang.Binding;

public class BSFEmbedGroovy{

 public static void main(String args[]) throws Exception {
  String fileName = "C:\\dev\\project\\src\\groovy\\
    com\\vanward\\groovy\\BusinessObjects.groovy";
  //this is required for bsf-2.3.0
  //the "groovy" and "gy" are extensions
  BSFManager.registerScriptingEngine("groovy", 
    "org.codehaus.groovy.bsf.GroovyEngine", new 
      String[] { "groovy" });
  BSFManager manager = new BSFManager();
  //DefaultGroovyMethods.getText just returns a 
  //string representation of the contents of the file
  manager.exec("groovy", fileName, 0, 0, 
    DefaultGroovyMethods.getText(new File(fileName)));
  }
}
</CODE></PRE></TD></TR></TBODY></TABLE>
<P><A name=N102D3><SPAN class=atitle2>Conclusion</SPAN></A><BR>If one thing is clear from this article, it's that Groovy presents a myriad of options for reuse inside your Java code. From compiling Groovy scripts into plain old Java .class files to dynamically loading and running scripts, the key aspects to consider are flexibility and coupling. Compiling Groovy scripts into normal .class files is the simplest choice for <I>using</I> the functionality you are trying to embed, but dynamically loading the scripts makes it easier to add or modify behavior without sacrificing time on compilation. (Of course, this option only works if the interface doesn't change.)</P>
<P>Embedding a scripting language in normal Java isn't an everyday occurrence, but opportunities do present themselves from time to time. In the examples I presented here, I embedded my simple directory-search utility in a Java-based application that could easily become an MP3 player application or some other MP3 utility. While I <I>could</I> have rewritten my MP3 file finder utility in Java code, I had no need to: Groovy is perfectly compatible with the Java language, and besides, I had lots of fun tinkering with all the options!</P><img src ="http://www.blogjava.net/mstar/aggbug/5724.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/mstar/" target="_blank">黑灵</a> 2005-06-08 12:36 <a href="http://www.blogjava.net/mstar/archive/2005/06/08/5724.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>alt.lang.jre: Nice 的双倍功能</title><link>http://www.blogjava.net/mstar/archive/2005/06/08/5716.html</link><dc:creator>黑灵</dc:creator><author>黑灵</author><pubDate>Wed, 08 Jun 2005 02:38:00 GMT</pubDate><guid>http://www.blogjava.net/mstar/archive/2005/06/08/5716.html</guid><wfw:comment>http://www.blogjava.net/mstar/comments/5716.html</wfw:comment><comments>http://www.blogjava.net/mstar/archive/2005/06/08/5716.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/mstar/comments/commentRss/5716.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/mstar/services/trackbacks/5716.html</trackback:ping><description><![CDATA[<!--StartFragment -->&nbsp;<SPAN class=atitle2>了解为何这门具有很强表达能力的语言最适合 Java 平台</SPAN><BR>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR vAlign=top align=left>
<TD>
<P>级别: 初级</P></TD></TR></TBODY></TABLE>
<P><A href="http://www-128.ibm.com/developerworks/cn/java/j-alj10064.html#author1"><NAME>Andrew Glover</NAME></A><BR>CTO, Vanward Technologies<BR>2004 年 10 月 06 日</P>
<BLOCKQUOTE><IMG height=38 alt="Column icon" src="http://www-128.ibm.com/developerworks/cn/i/c-j-jython.jpg" width=38 align=left border=0><ABSTRACT-EXTENDED>Nice 是可兼容 JRE 的、面向对象的语言，为 Java 平台提供了极强的语言表达能力。Nice 还允许在任何 Java 虚拟机上实现许多 Java 5 中的裁边功能。本文是 <I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">alt.lang.jre</I>系列的第 4 期，在这篇文章中，固定撰稿人，在各方面都很“Nice”的 Andrew Glover 将向您说明 Nice 的一些最令人激动的功能。 </ABSTRACT-EXTENDED></BLOCKQUOTE>
<P>Nice 是面向对象的、可兼容 JRE 的编程语言，重要特点是模块化、表达能力和安全。与纯粹面向对象的 Java 语言不同，Nice 合并了一些实用灵活的开发技术，其中包括面向方面编程中的一些技术。与许多较新的开发语言一样，Nice 的优点是通过改进以前的语言（包括 Java 语言）的缺点而来的。而且，Nice 不但提供了 Java 1.5 中的许多功能，而且使这些功能在任何 JVM 中都可以使用。</P>
<P>
<TABLE cellSpacing=0 cellPadding=5 width="30%" align=right border=1>
<TBODY>
<TR>
<TD background=/developerworks/cn/i/bg-gold.gif>
<P><A name=N1004D><B>关于本系列文章</B></A><BR>虽然 <A href="http://www.ibm.com/developerworks/views/java/articles.jsp?sort_order=desc&amp;expand=&amp;sort_by=Date&amp;show_abstract=true&amp;view_by=Search&amp;search_by=alt.lang.jre" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><I>alt.lang.jre</I> </A>系列的大多数读者都熟悉 Java 语言，以及它如何在跨平台的虚拟机上运行，但可能只有少数人知道 Java Runtime Environment 可以具有 Java 语言之外的语言。这里提到的大多数语言都是开放源代码，可以免费使用，只有少数是必须购买的商业产品。JRE 支持本系列文章中介绍的所有语言，作者相信这些语言可以增强 Java 平台的动态性和灵活性。 </P></TD></TR></TBODY></TABLE></P>
<P>在 <I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">alt.lang.jre</I>专栏的第 4 期中，我将向您介绍 Nice 的一些最引人注意的和最有用的功能，其中包括参数类、契约式设计构建、多方法等。 </P>
<P><A name=N10064><SPAN class=atitle3>Nice 入门</SPAN></A><BR>在 Java 语言中，功能的中心单元包含在类中。然而，Nice 将这个概念上升到了包的某个级别上。Nice 允许在以 <CODE>.nice</CODE> 后缀结尾的文件中定义多个类、接口和方法。一旦这样定义了文件，该文件通常就会变为一个包。Nice 的编译器 <CODE>nicec</CODE> 被用来编译 <CODE>.nice</CODE> 文件，该编译器也可以作为 Ant 任务或 Eclipse 插件使用（请参阅 <A href="http://www-128.ibm.com/developerworks/cn/java/j-alj10064.html#resources" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">参考资料</A>）。 </P>
<P>运行 Nice 应用程序需要定义 <CODE>main()</CODE> 方法。就像在 Java 语言中定义 <CODE>main()</CODE> 方法那样定义该方法。但是 Nice 的 <CODE>main</CODE> 方法与 Java 语言的不同，因为该方法是在包中定义的，不是在类中定义的。因此，该方法可以变为静态钩（static hook）来跳过 Nice 应用程序，与 Java 类中的 <CODE>static main()</CODE> 方法很像，它允许从命令行运行类。 </P>
<P>在清单 1 中，可以看到 Nice 的 <CODE>main()</CODE> 方法是如何工作的，清单 1 是验证一些基本匹配知识的简单应用程序。注意，Nice 支持 <CODE>assert</CODE> 关键字，即使是在 1.4 之前的 VM 上运行也是如此。 </P><A name=N1009B><B>清单 1. 简单 Nice 程序</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
void main(String[] args){ 
   assert 4 + 4 == 8;
}
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>正如一会将看到的，在 Nice 中，可以在类之外定义方法。在阅读本文之后，您就会对在包级别上定义方法的作用有更加清楚的认识。</P>
<P><A name=N100A8><SPAN class=atitle2>Nice 显示更加安全</SPAN></A><BR>代码安全是 Nice 可以引入开发工具箱的最强大的功能之一。当处理两个非常普遍的 Java 异常时，Nice 特别有效。这两个异常是： <CODE>ClassCastException</CODE> 和 <CODE>NullPointerException</CODE> 。 </P>
<P>这两个异常长久以来一直是从事 Java 平台工作的开发人员努力攻克的难题。Java 5.0 合并了常规类型，并以合并类型作为表达 <CODE>ClassCastExceptions</CODE> 的有效方式，但是这项更改对许多仍旧使用 Java 1.3 的公司不起作用。而另一方面，人们在考虑了 <CODE>ClassCastException</CODE> 和 <CODE>NullPointerException</CODE> 的情况下开发了 Nice。因此，Nice 语言还支持参数类和可选类型这两项功能，它们对阻止应用程序抛出异常大有帮助。而且，通过 Nice，现在可以在任何 Java 平台 1.2 或更高版本中使用这些功能。 </P>
<P><A name=N100C8><SPAN class=atitle2>参数类</SPAN></A><BR>在 Java 语言中，所有集合都有最少的共同类型，它就是 <CODE>Object</CODE> 。所以，Java 开发人员每次检索元素时，都必须转换类型，在较大的程序中，这会是一种负担。为了说明这个问题，我将向您展示 Java 语言是如何处理该类型，然后再向您展示 Nice 的参数类是如何简化这个问题的。 </P>
<P>清单 2 显示了 <CODE>IStack</CODE> 接口，用它来表示用 Java 语言定义的堆栈数据结构。 </P><A name=code2><B>清单 2. 用 Java 语言定义的 IStack 接口</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
public interface IStack {
  
  int size();
  
  boolean isEmpty();
 
  Object pop();
  
  Object peek();
  
  void push(Object obj);
}
</CODE></PRE></TD></TR></TBODY></TABLE>
<P><CODE>IStack</CODE> 的 Java 实现非常简单，但仍可用作堆栈数据结构，如清单 3 中所示。 </P><A name=code3><B>清单 3. IStack 实现</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
import java.util.ArrayList;
import java.util.List;

public class SimpleStack implements IStack {
  private List items;

  public SimpleStack(){
   this.items = new ArrayList();
  }

  public int size(){
    return this.items.size();
  }

  public boolean isEmpty(){
    return this.items.isEmpty();
  }

  public Object pop(){
    return this.items.remove(this.items.size() - 1);
  }

  public Object peek(){
    return this.items.get(this.items.size() - 1);
  }

  public void push(Object obj){
    this.items.add(obj);
  }
}
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>虽然非常有效，但是，只要使用这个堆栈实现，就需要进行单调乏味的类型转换，如清单 4 中所示。</P><A name=code4><B>清单 4. 显示非常耗时的类型转换的 JUnit 示例</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
import junit.framework.TestCase;

import org.age.nice.examples.stack.IStack;
import org.age.nice.examples.stack.SimpleStack;


public class SimpleStackUseTest extends TestCase {
  private IStack stack;

  public void testSimpleIntegerPops(){
    this.stack.push(new Integer(1));
    this.stack.push(new Integer(2));
    Integer two = (Integer)this.stack.pop();
    TestCase.assertEquals("value should be 2", 2, 
	   two.intValue());
  }

  public void testSimpleIntegerPopsWillNotCompile(){
    this.stack.push(new Integer(1));
    this.stack.push(new Integer(2));		
    //Integer two = this.stack.pop();		
    //TestCase.assertEquals("value should be 2", 2, two.intValue());
  }

  public void testPopClassCastException(){
    this.stack.push(new Integer(1));
    this.stack.push(new Double(2.0));
    try{
      Integer two = (Integer)this.stack.pop();
      TestCase.fail("top item was successfully cast to Integer!");
    }catch(ClassCastException e){
      //ignore expected behavior
    }		
  }

  protected void setUp() throws Exception {
    this.stack = new SimpleStack();
  } 
}
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>在清单 4 中， <CODE>testSimpleIntegerPops()</CODE> 方法显示了为何必须将 <CODE>pop</CODE> 操作的结果转换为 <CODE>Integer</CODE> 。如果未能执行转换，则会产生 <CODE>ClassCastException</CODE> ，如 <CODE>testPopClassCastException()</CODE> 方法中所述。仔细查看 <CODE>testSimpleIntegerPopsWillNotCompile</CODE> 方法中的注释行。它们没有使用普通 Java 代码进行编译；但该代码看起来不 <I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">好</I>吗？ </P>
<P><A name=N10122><SPAN class=atitle3>更好的集合处理</SPAN></A><BR>在其他功能中， <I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">参数类</I>或模板（如果从 C++ 后台开始）通常用于简化集合处理。参数类允许定义集合包含单一的、精确的类型。 </P>
<P>要用 Nice 定义普通的 <CODE>Stack</CODE> 实现，可以在定义类以及预期参数和返回类型时使用 <CODE>&lt;Type&gt;</CODE> 语法。在清单 5 中，可以看到如何通过使用 Nice 的 <CODE>&lt;Type&gt;</CODE> 语法的 <CODE>SimpleStack</CODE> 类来重新定义和简化堆栈数据结构。 </P><A name=code5><B>清单 5. Nice 参数堆栈</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
class SimpleStack&lt;Type&gt; {
  List&lt;Type&gt; items = new ArrayList();

  void push(Type t)  {
    items.add(t);
  }
 
  Type pop(){         
    return items.removeAt(items.size() - 1);
  }	
 
  Type peek(){
    return items.get(items.size() - 1);
  }
 	
  boolean isEmpty(){
    return items.isEmpty();
  }
 
  int size(){
    return items.size();
 } 
}
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>现在，我们定义了 <CODE>SimpleStack</CODE> 类参数化的 Nice 来存放特定参数类型，这些类型是在 <I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">编译时间</I>定义的，而不像在 Java 语言中那样，是在运行时定义的。注意清单 5 中的 <CODE>pop()</CODE> 和 <CODE>peek()</CODE> 方法是如何返回 <CODE>Type</CODE> 的，以及 <CODE>push()</CODE> 方法是如何将 <CODE>Type</CODE> 用作参数。该 <CODE>Type</CODE> 与内部 <CODE>items</CODE> 集合将包含的 <I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">对象类型</I>是相匹配的。 </P>
<P>清单 6 显示了在 Nice 中如何使用参数堆栈。注意，我已经创建了只能存放 <CODE>String</CODE> 类型的对象的 <CODE>SimpleStack</CODE> 实例。注意在 Nice 中为何不对最后一行进行编译！这个代码行试图 <CODE>push</CODE> 一个 <CODE>Integer</CODE> 到参数化堆栈中。 </P><A name=code6><B>清单 6. 在 Nice 中使用参数堆栈</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
 void main(String[] args){ 
    
   SimpleStack&lt;String&gt; stk = new SimpleStack();
   stk.push("Groovy");
   stk.push("Ruby");   

   assert stk.pop() == "Ruby";   
   assert stk.peek() == "Groovy";

   //following line won't compile! 
   //stk.push(new Integer(1));
 }
</CODE></PRE></TD></TR></TBODY></TABLE>
<P><A name=N10191><SPAN class=atitle2>可选类型</SPAN></A><BR><CODE>NullPointerException</CODE> 可能是所有 Java 开发人员最熟悉的异常。实际上，空指针非常麻烦，甚至是最简单的 Java 编译器都可以将对象标记为尚未初始化。不幸的是，您可能不时地发现，那些警告并没有捕获 <CODE>null</CODE> 的每次可能出现。 </P>
<P>为了与其标准安全属性一致，Nice 提供了 <I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">可选类型</I>概念。因为可选类型是 API 的作者使用 Java 语言定义的，常常难以确定，所以 Nice 为这些类型添加了问号（ <CODE>?</CODE> ）作为前缀。 </P>
<P>如果您发现自己经常开发使用大量参数的 API，那么可用选项将特别有用。确定不同的参数是可选的很可能是因为 <CODE>JavaDoc</CODE> 注释就是这样指定这些参数的，或者是因为您传递了 <CODE>null</CODE> 并指出其可以使用。 </P>
<P>使用 Nice，可以仅在参数之前添加 <CODE>?</CODE> 来表明变量 <I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">可能</I>为 <CODE>null</CODE> ；相反地，没有问号的变量不能直接设为 <CODE>null</CODE> 。在清单 7 中可以看到这是如何工作的，在该清单中，我在 <CODE>Dog</CODE> 类中定义了方法 <CODE>walk()</CODE> 。这表明类型 <CODE>Location</CODE> 的第二个参数可能为 <CODE>null</CODE> 。 </P><A name=code7><B>清单 7. 在 Nice 中定义可选类型</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
class Leash{
  int length;
}

class Location{
  String place;
}

class Dog{
  String name;

  void walk(Leash leash, ?Location location){

    var place = (location == null)? 
	    " no where " : " to " location.place;

    println("walking " name " with a leash " leash.length " inches long"
       //won't compile-&gt; location.place   
       place);
  }
}
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>注意清单 7 中的 <CODE>walk()</CODE> 方法为何 <I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">必须</I>说明 <CODE>location</CODE> 可以为 <CODE>null</CODE> 。在 <CODE>println()</CODE> 方法中，编译器将不允许代码实际引用 <CODE>location.place</CODE> 。 </P>
<P>清单 8 例示了可选类型的作用，本例中将使用清单 7 中定义的 <CODE>walk()</CODE> 方法。注意，现在可以合法传递 <CODE>null</CODE> ，还可以合法传递 <CODE>location</CODE> 的有效值。 </P><A name=code8><B>清单 8. Nice 中可选类型的示例</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
Leash lsh = new Leash(length:35);   
Dog mollie = new Dog(name:"Mollie");

mollie.walk(lsh, null);

Location loc = new Location(place:"The Coffee Shop");

mollie.walk(leash:lsh, location:loc);
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>不要将空功能语法（null capability syntax）与 <I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">可选参数</I>混淆，它们是完全不同的。 </P>
<P><A name=N1021C><SPAN class=atitle2>命名和可选参数</SPAN></A><BR>正如您在清单 8 中可能注意到的，在调用 <CODE>walk()</CODE> 方法时，Nice 允许指定参数。当我在 <CODE>Dog</CODE> 实例中调用 <CODE>walk()</CODE> 方法时，我明确命名了参数。例如，将类型 <CODE>Leash</CODE> 的 <CODE>lsh</CODE> 变量设为第一个参数 <CODE>leash</CODE> 。 </P>
<P>Nice 还允许在构造函数中指定参数，与在 Groovy 和 Jython 等语言中的做法非常类似。在清单 8 中，当创建新的 <CODE>Dog</CODE> 和 <CODE>Leash</CODE> 的实例时，我在构造函数中分别明确设置了每个实例的属性、名称和长度。 </P>
<P>命名参数允许您以任何希望的顺序传递这些参数。例如，在清单 9 中，这两个调用基本相同；因为我命名了参数，传递这些参数的顺序没有影响。 </P><A name=code9><B>清单 9. Nice 中可选类型的进一步说明</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
mollie.walk(leash:lsh, location:loc);
//same behavior from walk method
mollie.walk(location:loc, leash:lsh);
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>可选参数甚至比可选类型更有用。它们实际上可以替代可选类型。清单 10 显示了如何使用可选参数重新定义 <CODE>walk()</CODE> 方法。 </P><A name=code10><B>清单 10. Nice 中的可选参数</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
void walkAgain(Leash leash, Location location=new Location(place:"nowhere")){        
  println("walking (again) " name " with a leash " leash.length 
     " inches long to " location.place);
}
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>该代码将第二个参数 <CODE>location</CODE> 定义为 <I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">可选</I>。调用 <CODE>walk()</CODE> 方法时，不必传送该参数。如果未传送任何值，那么将使用默认值。在本例中，默认值是 <CODE>Location</CODE> ，其中 <CODE>place</CODE> 等于 <I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">nowhere</I>。 </P>
<P>与 <A href="http://www-128.ibm.com/developerworks/cn/java/j-alj10064.html#code7" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">清单 7</A> 中所做相同，可选参数使您不必进行防御性地编程。但与清单 7 中的 <CODE>walk()</CODE> 方法不同的是，编写清单 10 中的 <CODE>walkAgain()</CODE> 方法需要考虑遇到 <CODE>null</CODE> 值的可能性。 </P>
<P>对于可选参数，最后要注意的一点是，可以使用其他值覆盖这些参数。如清单 11 中所示，可以选择一个 <I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">或</I>两个参数来调用 <CODE>walkAgain</CODE> 。 </P><A name=code11><B>清单 11. 覆盖 Nice 中的可选参数</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
mollie.walkAgain(lsh);
Location locBY = new Location(place:"the backyard");
mollie.walkAgain(lsh, locBY);
</CODE></PRE></TD></TR></TBODY></TABLE>
<P><A name=N102A6><SPAN class=atitle2>契约式设计</SPAN></A><BR><I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">契约式设计</I>（DBC）是一项技术，它通过在每个组件的界面中明确说明该组件的预期功能和客户机的异常，来确保系统中的所有组件执行它们将进行的操作。Eiffel（请参阅 <A href="http://www-128.ibm.com/developerworks/cn/java/j-alj10064.html#resources" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">参考资料</A>）是一种使用 DBC 的流行语言。许多语言都已经合并了契约式设计技术，其中包括 Java 1.4，该技术引入了断言的使用。Nice 使用关键字 <CODE>requires</CODE> 和 <CODE>ensures</CODE> 来合并程序和断言中的契约信息，以便在执行过程中确认该程序的状态。另外，Nice 甚至支持 1.4 之前的 JVM 使用 <CODE>assert</CODE> 关键字。 </P>
<P><CODE>requires</CODE> 和 <CODE>ensures</CODE> 关键字的作用与前条件和后条件相似，它们允许定义预期的方法将遵循和保证的条件。如果条件得不到满足，将会在运行时生成断言异常。条件和断言的组合已经使许多应用程序免于陷入由逻辑错误引起的深渊中。我将在下面的实例中说明如何一起使用这两种机制。 </P>
<P><A name=N102CD><SPAN class=atitle3>条件性使用</SPAN></A><BR><CODE>requires</CODE> 子句说明了某个方法的客户机必须满足的要求。如果未满足要求，那么将放弃该方法，并生成断言异常。 <CODE>ensures</CODE> 子句将在方法的客户机端起作用。该子句是将方法提交到其相关联的调用者的保证。 </P>
<P>清单 12 定义了包含 <CODE>brew()</CODE> 方法的 <CODE>CoffeeMachine</CODE> 类。在 <CODE>brew()</CODE> 的定义中，要求客户机 <I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">必须</I>传递 <CODE>Coffee</CODE> 实例，其中 <CODE>beanAge</CODE> 属性小于 10。否则，将生成与使用子句“Beans are too old to brew”一致的断言异常。而且，我还保证方法（本例中为 <CODE>CoffeeCup</CODE> 的实例）的 <CODE>result</CODE> 将具有大于 155 的 <CODE>temp</CODE> 属性。 </P><A name=code12><B>清单 12. DBC 确保理想 brew</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
class CoffeeMachine{

  CoffeeCup brew(Coffee cfe) 
    requires cfe.beanAge &lt; 10 : "Beans are too old to brew"
    ensures result.temp &gt; 155 : "Coffee isn't hot enough to serve" {		

      return new CoffeeCup(coffee: cfe, temp:160, isFull:true);
   }
}

class Coffee{
  int beanAge;
}

class CoffeeCup{
  int temp;
  boolean isFull;
  Coffee coffee;
}
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>清单 13 显示了新的 <CODE>CoffeeMachine</CODE> 实例和 <CODE>Coffee</CODE> 的新实例。注意，在本例中，已经将 <CODE>Coffee</CODE> 的 <CODE>beanAge</CODE> 属性设为 15，该值大于 10，因此不满足 <CODE>brew</CODE> 的契约。 </P><A name=code13><B>清单 13. 契约违反</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
CoffeeMachine machine = new CoffeeMachine();
Coffee cfe = new Coffee(beanAge:15);
CoffeeCup cup = machine.brew(cfe);
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>清单 14 说明了在清单 13 中调用 <CODE>brew()</CODE> 时生成的异常堆栈。正如您可以看到的，这里提供了定制消息“Beans are too old to brew”来帮助调试。 </P><A name=code14><B>清单 14. These beans are too old to brew!</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
Exception in thread "main" nice.lang.AssertionFailed: Beans are too old to brew
  at test.dispatch.brew(MoreCoffee.nice:6)
  at test.fun.main(HelloWorld.nice:111)
  at test.dispatch.main(MoreCoffee.nice:0)
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>默认情况下，不启用 Nice 断言，所以必须开启这些断言。有关 JVM 的指令，请参阅 Nice 的文档。</P>
<P><A name=N10343><SPAN class=atitle3>重访堆栈</SPAN></A><BR>在基本了解了 Nice 如何实现前条件和后条件之后，让我们看一下将这些技术应用于前面的 <CODE>IStack</CODE> 示例时会发生什么。在清单 15 中，用 Nice 定义了 <CODE>IStack</CODE> 接口，并添加了不同的 <CODE>ensures</CODE> 和 <CODE>requires</CODE> 子句。 </P><A name=code15><B>清单 15. 添加了条件的 IStack 接口</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
interface IStack&lt;T&gt;{
  int size() ensures result &gt;= 0 : "size can not be less than one";
	
  void push(T t) ensures size(this) &gt; 0 : "pushing an item should increase the size";	
	
  boolean isEmpty() ensures result == (size(this) == 0) : "if size is zero, result should be false";	

  T pop() requires !isEmpty(this) : "Can not pop an empty stack";

  T peek() requires !isEmpty(this) : "Can not pop an empty stack";
}
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>在清单 16 中，实现了 <CODE>IStack</CODE> 接口。注意，Nice 为定义方法本体提供了捷径：只使用 <CODE>=</CODE> 语法。还要注意下例中 Nice 的 <CODE>override</CODE> 语法。 </P><A name=code16><B>清单 16. 新的改进的堆栈</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
class DBCStack&lt;T&gt; implements IStack{

  ArrayList&lt;T&gt; contents = new ArrayList();

  override void push(T t) = contents.add(t);
  
  override T peek() = contents.get(contents.size() - 1);    
  
  override T pop() = contents.removeAt(contents.size() - 1);  
  
  override boolean isEmpty() = contents.size() == 0;
    
  override int size() = contents.size();

}
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>在很大程度上，可以像以前那样使用新的 <CODE>DBCStack</CODE> 。不过，如果试图违反 <CODE>IStack</CODE> 契约的条款，将会引起断言异常。例如，在清单 17 中，可以看到当试图对已经确保两个项安全的堆栈中推入第三个项时会发生什么。第三个 <CODE>pop()</CODE> 的调用导致先条件 <CODE>requires !isEmpty(this)</CODE> 失败。因此，将生成 <CODE>AssertionFailed</CODE> 异常以及定制消息：“cannot pop an empty stack”。 </P><A name=code17><B>清单 17. 测试新堆栈的限制</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
let IStack&lt;Dog&gt; dbcStack = new DBCStack();

dbcStack.push(new Dog(name:"Stella"));
dbcStack.push(new Dog(name:"Mollie"));
    
println(dbcStack.pop().name);//mollie
println(dbcStack.pop().name);//stella

// throws assertion error -&gt; println(dbcStack.pop().name);
// Exception in thread "main" nice.lang.AssertionFailed: Can not pop an empty stack
/   at test.dispatch.pop(StackImpl.nice:10)
//  at test.fun.main(HelloWorld.nice:129)
//  at test.dispatch.main(StackImpl.nice:0)
</CODE></PRE></TD></TR></TBODY></TABLE>
<P><A name=N103A0><SPAN class=atitle2>多方法的好处</SPAN></A><BR>Nice 最引人注意的独特功能之一是 <I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">多方法</I>，或在特定类定义之外定义类实例方法的能力。该方法独自创建了许多扩展，这些扩展可与面向方面编程（AOP）的一些比较令人激动的原则相媲美。 </P>
<P>方法的语法相当简单，因为第一个参数是方法应该附加的类型。剩余的参数则成为实例方法的标准参数。为了举例说明，在清单 18 中，可以创建简单的、无意义的方法，并将其附加到 <CODE>java.lang.String</CODE> 的实例。 </P><A name=code18><B>清单 18. Nice 中的多方法</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
void laugh(String str){
  println("haha, I'm holding an instance of " str);
}
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>在该代码中，当调用 <CODE>laugh()</CODE> 方法时，它将只打印 <CODE>String</CODE> 。因为 <CODE>laugh()</CODE> 方法的第一个参数的类型为 <CODE>String</CODE> ，因此，要将该方法附加到 <CODE>String</CODE> 的实例中。在清单 19 中，创建 <CODE>String</CODE> 实例 <CODE>myString</CODE> ，并调用 <CODE>laugh()</CODE> 方法，该方法将打印“haha, I'm holding an instance of Andy”。 </P><A name=code19><B>清单 19. 使用 Nice 中的多方法</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
let myString = new String("Andy");
myString.laugh();
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>虽然 <CODE>laugh()</CODE> 方法完全无用，但确实说明了几个关键点： </P>
<UL xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<LI>Nice 使您能够轻松地将新的行为附加到对象中。</LI>
<LI>Nice 允许将该行为附加到任何事物，其中包括您无法访问其源代码的标准类。</LI>
<LI>Nice 使您可以将行为添加到 <CODE>final</CODE> 对象。 </LI></UL>
<P><A name=N10401><SPAN class=atitle3>高级多方法</SPAN></A><BR>将有用的行为添加到对象比使用无用的行为更有意义，所以让我们将参数类和多方法的知识提高到另一个级别。在清单 20 中，您将看到当将 <CODE>join()</CODE> 方法添加到 <CODE>java.util.Collection</CODE> 接口时会发生什么。 <CODE>join()</CODE> 方法在敏捷语言之间非常通用；它只将预期的 <CODE>String</CODE> 附加到集合的所有元素中，以创建大的 <CODE>String</CODE> 。 </P><A name=code20><B>清单 20. 将 join 方法添加到 Collection 接口中</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
/**
 * multi method, adds a join call to a collection. 
 * @return a string like 1-2-3-4. 
 */
&lt;T&gt; String join (Collection&lt;T&gt; collection, String value = " "){
  StringBuffer buff = new StringBuffer();
  let size = collection.size();
  var x = 0;
  for (T elem : collection){
    buff.append(elem);
    if(++x &lt; size){
      buff.append(value);
    }
  }
  return buff.toString();  
}
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>清单 20 说明了如何使用 Nice 的多方法功能将 <CODE>join()</CODE> 方法附加到任何类型的 <CODE>Collection</CODE> 中。在本例中， <CODE>join</CODE> 方法包含可选参数 <CODE>String</CODE> ，使用该参数，可以连接在其上调用 <CODE>join()</CODE> 方法的 <CODE>Collection</CODE> 实例的元素。 </P>
<P>清单 21 显示，使用新的 <CODE>join()</CODE> 方法非常容易。它只传递预期的 join <CODE>String</CODE> 或使用默认值。这项功能就像 AOP 中的静态横切（static crosscutting），但是 Nice 版本看起来 <I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">容易得多</I>！ </P><A name=code21><B>清单 21. 非常好的新连接方法！</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
Collection&lt;int&gt; nColl = new ArrayList();
nColl.add(1);
nColl.add(3);
nColl.add(3);

println(nColl.join("**")); //prints 1**3**3
println(nColl.join()); //prints 1 3 3
</CODE></PRE></TD></TR></TBODY></TABLE>
<P><A name=N1045B><SPAN class=atitle2>抽象接口</SPAN></A><BR>除了多方法之外，Nice 还提供了将其他行为附加到对象的第二种方法。 <I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">抽象接口</I>与普通 Java 接口相似，但要灵活得多。抽象接口最好的地方是可以由任何对象实现，即使定义了接口之后也可以。在这点上，抽象接口功能与 AOP 中的静态横切非常相像。 </P>
<P>在清单 22 中，可以开始了解抽象接口如何工作。我先创建类型 <CODE>TasteTest</CODE> 的新抽象接口，它包含一个方法 <CODE>taste()</CODE> 。然后使 <CODE>Mocha</CODE> 和 <CODE>Latte</CODE> 类实现这个新类型。 </P><A name=code22><B>清单 22. Nice 中的抽象接口</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
package test;

class Latte {
  getPrice() = new BigDecimal(2.50);
}

class Mocha {
  getPrice() = new BigDecimal(4.30);
}

abstract interface TasteTest{
   void taste();
}

class test.Mocha implements TasteTest;
class test.Latte implements TasteTest;

taste(test.Mocha mcha) = println("Ohh this is good...");
taste(Latte lte) = println("Waking me up it's soooo good.");

</CODE></PRE></TD></TR></TBODY></TABLE>
<P>在 <CODE>Latte</CODE> 的实例上使用新的功能非常简单，如清单 23 中所示。只需调用 <CODE>taste</CODE> 方法即可！ </P><A name=code23><B>清单 23. Latte 唤醒调用</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
let coffee = new Latte();	
coffee.taste(); //prints Waking me up it's soooo good.
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>虽然使用多方法和抽象接口的实例相当简短，但它们确实说明了使用 Nice 可以达到的表达能力级别。实际上，一些人认为，由 Nice 语法的简易性所支持的 Nice 的表达能力能够与 Java 编程中的 AOP 相媲美。</P>
<P><A name=N1049C><SPAN class=atitle2>好用的枚举类型</SPAN></A><BR>正如前面所说的，Nice 合并了 Java 5.0 中的一些功能，实际上，它现在允许在任何 Java 平台上使用这些功能。其中一项功能是枚举类型。与参数类相同，枚举类型在编译时而不是在运行时帮助您进行 bug 检测。</P>
<P>为了了解枚举类型是如何工作的，我将使用一个通用开发实例。常量和关键字通常以 <CODE>static final</CODE> 字段形式放置在接口和类中。然后，其他类可以引用这些字段，而不是引用本地变量，以限制发生的变化。定义常量的代码与清单 24 中的相似。 </P><A name=code24><B>清单 24. Java 常量示例</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
public class CoffeeBeans {
    public static final int ESPRESSOROAST = 1;
    public static final int KONA = 2; 
    public static final int FRENCHROAST = 3; 
    public static final int MOCHA = 4;
}
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>清单 25 显示了使用中的常量类型的典型示例。如果仔细查看该代码，您还会发现有损其有效性的地方。</P><A name=code25><B>清单 25. Java 常量的限制</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
public static void brew(int coffeeType){    	    
  if(coffeeType == CoffeeBeans.ESPRESSOROAST){
    System.out.println("brewing espresso!");
  }  
  //other if/else clauses....
}
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>清单 25 中代码的问题在于：恶意的或无知的客户机可以使用制定的限制之外的值来调用 <CODE>brew()</CODE> 方法。例如，如果使用 <CODE>48</CODE> 调用该方法，代码的编译将很完美。但不幸的是，当运行代码时，仍然必须处理这个问题。 </P>
<P>使用 <I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">枚举</I>而不是使用常量会使代码更加安全。实质上，枚举将强制编译器确保使用的值在定义的限制内。例如，在清单 26 中，可以看到为 <CODE>CoffeeBeanType</CODE> 定义枚举时会发生什么，同时还定义了类型 <CODE>ICoffee</CODE> 的接口和两个实现： <CODE>Latte</CODE> 和 <CODE>Mocha</CODE> 。 </P>
<P>正如您所看到的，这些 <CODE>ICoffee</CODE> 类型定义返回枚举实例的 <CODE>getType()</CODE> 方法。您还可以看到的是，如果定义 <CODE>CoffeeMachine</CODE> 类并包含枚举实例，而不是定义如清单 25 中所示的包含 <CODE>int</CODE> 的方法，那么将会发生什么。 </P><A name=code26><B>清单 26. 在 Nice 中定义枚举</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
enum CoffeeBeanType(String value){ 
  ESPRESSOROAST("espresso"), 
  KONA("kona"), 
  FRENCHROAST("French Roast"), 
  MOCHA("Mocha Java")
}

interface ICoffee{
  CoffeeBeanType getType();
  BigDecimal getPrice();
}

class Latte implements ICoffee{
  getType()= ESPRESSOROAST;
  getPrice() = new BigDecimal(2.50);
}

class Mocha implements ICoffee{
  getType()= FRENCHROAST;
  getPrice() = new BigDecimal(4.30);
}

class CoffeeMachine{
}

void brew(CoffeeMachine machine, CoffeeBeanType type){
 println("Brewing a coffee with " type.value " beans" );
}
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>在清单 27 中，当对 <CODE>CoffeeMachine</CODE> 实例调用 <CODE>brew()</CODE> 方法时，要使用枚举类型。注意在 Nice 中枚举是如何隐含 <CODE>value</CODE> 字段的。在 <CODE>ESPRESSOROAST</CODE> 中，该值为创建时传递的 <CODE>String</CODE> ： <CODE>espresso</CODE> 。 </P><A name=code27><B>清单 27. 在 Nice 中使用枚举</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
let cfe = new CoffeeMachine();
cfe.brew(ESPRESSOROAST); 
//prints Brewing a coffee with espresso beans
	
let coffee = new Latte();	
println("\n My latte cost me $" coffee.getPrice() 
  " and is brewed with " coffee.getType().value " beans");
</CODE></PRE></TD></TR></TBODY></TABLE>
<P><A name=N10526><SPAN class=atitle2>高级集合处理</SPAN></A><BR>Nice 是一种避开转换对象概念的强类型语言，所以必须将 Nice 中的所有集合都参数化。例如，下面的行通常将使用 Java 语言进行编译，但在 Nice 中，集合必须是更强类型的。 </P>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
Collection noColl = new ArrayList(); //will not compile in Nice
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>如果因为某种原因，需要将 Nice 集合呈现得更像 Java，那么可以仅使用 <CODE>java.lang.Object</CODE> 将该集合参数化，如下所示。 </P>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
Collection&lt;Object&gt; collObj = new ArrayList();
</CODE></PRE></TD></TR></TBODY></TABLE>
<P><A name=N1053E><SPAN class=atitle3>多方法和集合</SPAN></A><BR>与其他一些语言类似，Nice <I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">使用多方法</I>向标准集合添加了其他许多方法。由于 Nice 的集合行为支持块语法，所以这些行为与 Groovy 和 Ruby 中的类似。虽然这些代码块其实只是匿名方法，不像真实的闭包（true closures）那样强大，但它们实际上更方便。 </P>
<P>Nice 在集合中提供了灵活的类迭代器（iterator-like）方法，该方法名为 <CODE>foreach()</CODE> ，如清单 28 中所示。注意 <CODE>foreach()</CODE> 方法是如何拥有由 <CODE>=&gt;</CODE> 表示的代码块的。在本例中，该代码块只打印 <CODE>i</CODE> 将包含的值。 </P><A name=code28><B>清单 28. 集合中的 foreach 方法</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
Collection&lt;Integer&gt; coll = new ArrayList();
coll.add(new Integer(1));
coll.add(new Integer(2));
coll.add(new Integer(3));

coll.foreach(Integer i =&gt; {
  println(i);
}); //prints 1 2 3
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>Nice 还通过一些非常好的方法增强了 Java 语言的 <CODE>Set</CODE> 接口，如清单 29 中所示。 </P><A name=code29><B>清单 29. Nice 中非常好用的 Set 方法</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
Set&lt;int&gt; testSet = new HashSet();
Set&lt;int&gt; otherSet = new HashSet();

testSet.add(1);
testSet.add(2);
testSet.add(3);

otherSet.add(3);
otherSet.add(4);
otherSet.add(5);

Set&lt;int&gt; nSet = testSet.intersection(otherSet);   
nSet.foreach(int i =&gt; println(i)); //prints 3   

Set&lt;int&gt; uSet = testSet.union(otherSet);   
uSet.foreach(int i =&gt; println(i)); //prints 1,2,3,4,5

Set&lt;int&gt; difSet = testSet.difference(otherSet);   
difSet.foreach(int i =&gt; println(i)); //prints 1,2

Set&lt;int&gt; dSet = testSet.disjunction(otherSet);   
dSet.foreach(int i =&gt; println(i)); //prints 1,2,4,5
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>正如在上面的代码中可以看到的，Nice 提供了 <CODE>intersection()</CODE> 方法，该方法将查找两个单独 <CODE>Set</CODE> 中的共同元素。 <CODE>union()</CODE> 方法合并这两个 <CODE>Set</CODE> ，而 <CODE>difference()</CODE> 方法查找这两个 <CODE>Set</CODE> 之间的差异。最后， <CODE>disjunction()</CODE> 方法合并了这两个 <CODE>Set</CODE> ，同时还删除它们的共同元素。 </P>
<P><A name=N1059B><SPAN class=atitle2>其他功能</SPAN></A><BR>在要结束对 Nice 的介绍时，我将讲述一下它的三个比较引人注意的便利功能：值分发、高级 <CODE>for</CODE> 循环和范围，以及更随意的 <CODE>String</CODE> 使用。正如在前面一小节中介绍的那样，您将注意到每项功能都提高了代码的表达能力和模块化，同时还增强了代码安全。 </P>
<P><A name=N105AC><SPAN class=atitle3>值分发方法</SPAN></A><BR><I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">值分发方法</I>被用来确保实际调用或分发到哪个方法的运行时决策不仅由参数的类型确定，还由参数的实际 <I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">值</I>确定。该方法有助于避免代码包含一系列 <CODE>if</CODE> / <CODE>else</CODE> 子句或 <CODE>switch</CODE> 语句。 </P>
<P>在清单 30 中可以看到该方法是如何工作的。我先为音乐类型定义了一个 <CODE>enum</CODE> 。然后创建了一系列有效模拟 <CODE>switch</CODE> 语句的值分发方法。如果这时传递 <CODE>Genre</CODE> 值 <I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">Celtic</I>， <CODE>variationName()</CODE> 方法将返回“Irish”，如下所示。 </P><A name=code30><B>清单 30. 详细定义的值分发</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
enum Genre(String value) { 
  Celtic("Celtic"), Rock("Rock"), Folk("Folk"), Jazz("Jazz") 
}

String variationName(Genre gre);
variationName(gre) = "No variations Available";
variationName(Celtic) = "Irish";
variationName(Folk) = "Acoustic";
variationName(Rock) = "Pop";
variationName(Jazz) = "Smooth Jazz";
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>如下面清单 31 中所示，当传递 <CODE>Genre enum</CODE> 值 <CODE>Folk</CODE> 时，由此得到的 <CODE>variation</CODE> 变量将被设为 <CODE>Acoustic</CODE> 。 </P><A name=code31><B>清单 31. 在 Nice 中使用值分发</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
var variation = variationName(Folk);
println(variation); //prints Acoustic
</CODE></PRE></TD></TR></TBODY></TABLE>
<P><A name=N10604><SPAN class=atitle3>增强的 for 循环和范围</SPAN></A><BR>这时，您可能已经注意到，Nice 支持 <CODE>for</CODE> 循环标准的简略概念，这与新的 Java 5 非常像。简单的 <CODE>for</CODE> 循环构造在敏捷语言之间非常通用，奇怪的是，Java 语言居然用了这么长时间来引入它！ </P>
<P>如清单 32 中所示，Nice 使您可以通过 <CODE>int</CODE> 的集合轻松进行迭代，而无需使用 Java 语言常用的 <CODE>Iterator</CODE> 接口。还要注意的是，Nice 支持自动置入，与 Groovy 很像（请参阅 <A href="http://www-128.ibm.com/developerworks/cn/java/j-alj10064.html#resources" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">参考资料</A>，以获得关于 Groovy 的 <I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">alt.lang.jre</I>期刊）。 </P><A name=code32><B>清单 32. Nice 的 for 循环</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
Collection&lt;int&gt; iColl = new ArrayList();
iColl.add(11);
iColl.add(12);

for(int i : iColl){
  println(i);
}	
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>清单 33 说明了 Nice 中的范围功能。Nice 仅支持包括的范围；因此，在下面的代码中，将打印从 1 到（且包括）20 的数字。</P><A name=code33><B>清单 33. Nice 中的包括范围</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
for(int i : 1..20){
  print(i);
}
</CODE></PRE></TD></TR></TBODY></TABLE>
<P><A name=N1063E><SPAN class=atitle3>随意字符串</SPAN></A><BR>Nice 提供了普通 Java <CODE>String</CODE> ，但放宽了与这些字符串在 Java 语言中的使用相关联的一些限制。如果曾使用过 Python，您将认识 Nice 的多行字符串常量。在清单 34 中，可以看到使用 <CODE>"""</CODE> 语法创建多行字符串非常容易。您还将注意到 <CODE>'</CODE> 如何自动转义。 </P><A name=code34><B>清单 34. Nice 中的多行字符串</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
var poem = """ 
   This is a multiline String.
   Why wouldn't anyone want to make one?
   """;

println(poem);

var line1 = "All roads lead to where you are";
var line2 = "Love don't need to find a way";
var concat = "Da da " line1 " da da " line2;

println(concat);

println("concat " line1 " with " line2);

</CODE></PRE></TD></TR></TBODY></TABLE>
<P>Nice 还通过放宽 Java 语言标准 <CODE>+</CODE> 语法的限制，使字符串连接更加容易。因此，可以调用 <CODE>println()</CODE> 并删除相关联的串联，如上面清单 34 中所示。 </P>
<P><A name=N10668><SPAN class=atitle3>结束语</SPAN></A><BR>Java 语言的 5.0 版中包含本月 <I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">alt.lang.jre</I>期刊中讲述的许多最好的功能。不过，并不是每个人都可以立即使用 Java 5，其余一些人可以使用 Nice。除了使您可以实际地在任何 JVM 版本上使用参数类、多方法、契约式设计和其他许多便利功能之外，Nice 还使您初步了解了在所有开发平台上的表达能力和敏捷度的优点。正如这里所展示的，当提到在 Java 平台上构造更安全的、更模块化的代码时，Nice 是很好的选择。请参阅 <A href="http://www-128.ibm.com/developerworks/cn/java/j-alj10064.html#resources" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">参考资料</A>部分，以了解关于 Nice 的更多信息，同时还请耐心等待下月的 <I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">alt.lang.jre</I>期刊，它将介绍 Rhino。 </P><img src ="http://www.blogjava.net/mstar/aggbug/5716.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/mstar/" target="_blank">黑灵</a> 2005-06-08 10:38 <a href="http://www.blogjava.net/mstar/archive/2005/06/08/5716.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>alt.lang.jre: 感受 Groovy</title><link>http://www.blogjava.net/mstar/archive/2005/06/08/5714.html</link><dc:creator>黑灵</dc:creator><author>黑灵</author><pubDate>Wed, 08 Jun 2005 02:19:00 GMT</pubDate><guid>http://www.blogjava.net/mstar/archive/2005/06/08/5714.html</guid><wfw:comment>http://www.blogjava.net/mstar/comments/5714.html</wfw:comment><comments>http://www.blogjava.net/mstar/archive/2005/06/08/5714.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/mstar/comments/commentRss/5714.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/mstar/services/trackbacks/5714.html</trackback:ping><description><![CDATA[<!--StartFragment -->&nbsp;<SPAN class=atitle2>介绍 Java 平台的一种新标准语言</SPAN><BR>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR vAlign=top align=left>
<TD>
<P>级别: 初级</P></TD></TR></TBODY></TABLE>
<P><A href="http://www-128.ibm.com/developerworks/cn/java/j-alj08034/index.html#author1"><NAME>Andrew Glover</NAME></A><BR>CTO, Vanward Technologies<BR>2004 年 8 月 03 日</P>
<BLOCKQUOTE><IMG height=38 alt="Column icon" src="http://www-128.ibm.com/developerworks/cn/i/column_icon.gif" width=38 align=left border=0><ABSTRACT-EXTENDED>虽然 Java 语言因其严密性和扩展性的承诺而在整整一代程序员中胜出，但是 Groovy 预示了 Java 平台上的一个编程新时代，这种语言是以方便性、适宜性和敏捷性为出发点定义的。在新的 <I xmlns:dw="http://www.ibm.com/developerworks/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">alt.lang.jre</I>专栏的第二期文章中，Andrew Glover 对提议添加到 Java 平台的标准编程语言作了非正式的介绍。 </ABSTRACT-EXTENDED></BLOCKQUOTE>
<P>如果您在使用 Java 平台（block），不管时间长短，您都有可能听说过 Groovy。Groovy 是超级明星开发人员 James Strachan 和 Bob McWhirter 发明的，它是一种敏捷开发语言，完全以 Java 编程 API 为基础。Groovy 当前正处于 Java Specification Request 的开始阶段，它于 2004 年 3 月底获得批准。Groovy 还是一种脚本语言，有些人说它会永久性地改变您看待和使用 Java 平台的方式。</P>
<P>在其对 JSR 241 （请参阅 <A href="http://www-128.ibm.com/developerworks/cn/java/j-alj08034/index.html#resources" xmlns:dw="http://www.ibm.com/developerworks/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">参考资料</A>）的开放评论中，Groovy 的共同规范领导者 Richard Monson-Haefel 说他对 Groovy 的支持是建立在总有一天 Java 平台要包括一种敏捷开发语言这一信念上。与许多移植到 Java 平台的脚本语言不同，Groovy 是 <I xmlns:dw="http://www.ibm.com/developerworks/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">为</I>JRE 而写的。在规范请求中（参阅 <A href="http://www-128.ibm.com/developerworks/cn/java/j-alj08034/index.html#resources" xmlns:dw="http://www.ibm.com/developerworks/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">参考资料</A>），Groovy 的制造者提出了“Java 不仅是一种编程语言，更是一个健壮的平台，可以有多种语言在上面运行和共存”（Monson-Haefel 语）的思想。 </P>
<P>新 <I xmlns:dw="http://www.ibm.com/developerworks/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">alt.lang.jre</I>专栏的这第二期文章的目的是让读者了解 Groovy。我首先回答关于这种新语言的一些最显然的问题（为什么需要它？），然后以代码为基础概述 Groovy 最令人兴奋的功能。 </P>
<P><A name=N10065><SPAN class=atitle2>为什么需要另一种语言？</SPAN></A><BR>正如在 <A href="http://www-128.ibm.com/developerworks/cn/java/j-alj07064/" xmlns:dw="http://www.ibm.com/developerworks/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">上个月的专栏</A>中介绍的，Groovy 不是与 JRE 兼容的惟一脚本语言。Python、Ruby 和 Smalltalk 就是成功地移植到 Java 平台上的三种脚本语言。对于一些开发人员，这带来了问题：为什么要另一种语言？毕竟，我们许多人已经将 Java 代码与 Jython 或者 JRuby 结合来快速开发应用程序，为什么还要学习另一种语言？回答是 <I xmlns:dw="http://www.ibm.com/developerworks/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">您不一定要学习一种新语言以用 Groovy 编码</I>。Groovy 与其他 JRE 兼容脚本语言的不同在于它的语法以及重用 Java 库。Jython 和 JRuby 共享它们前身（分别是 Python 和 Ruby）的外观，Groovy 让人觉得就像是 Java 语言，不过限制要少得多。 </P>
<P>
<TABLE cellSpacing=0 cellPadding=5 width="40%" align=right border=1>
<TBODY>
<TR>
<TD background=/developerworks/cn/i/bg-gold.gif>
<P><A name=N10078><B>关于本系列</B></A><BR>虽然本系列的大多数读者熟悉 Java 语言以及它是如何在跨平台虚拟机上运行的，但是只有少数人知道 Java Runtime Environment 可以承载 Java 语言之外的语言。本系列文章对 JRE 的多种替代语言进行了综述。这里讨论的大多数语言是开放源代码的，许多是免费使用的，有少数是商业产品，必须购买。在 <A href="http://www.ibm.com/developerworks/views/java/articles.jsp?sort_order=desc&amp;expand=&amp;sort_by=Date&amp;show_abstract=true&amp;view_by=Search&amp;search_by=alt.lang.jre" xmlns:dw="http://www.ibm.com/developerworks/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><I>alt.lang.jre</I> </A>系列中介绍的所有语言都得到了 JRE 支持，并且作者相信它们增强了 Java 平台的动态性和灵活性特征。 </P></TD></TR></TBODY></TABLE></P>
<P>像 Jython 这样的语言是在它们的父语言库上建立的，而 Groovy 使用了 Java 开发人员最熟悉的功能和库 <I xmlns:dw="http://www.ibm.com/developerworks/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">—— 但是将它们放到了一个敏捷开发框架中</I>。敏捷开发的基本宗旨是代码应该很好地适合范围广泛的任务，并可以不同的方式应用。Groovy 通过以下方式落实了这些宗旨： </P>
<UL xmlns:dw="http://www.ibm.com/developerworks/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<LI>使开发人员不用编译。</LI>
<LI>允许动态类型。</LI>
<LI>使合成结构容易。</LI>
<LI>使其脚本可以在普通 Java 应用程序中使用。</LI>
<LI>提供一个 shell 解析器。</LI></UL>
<P>这些特性使 Groovy 成为一种特别容易学习和使用的语言，不管您是有经验的 Java 开发人员还是刚接触 Java 平台。在下面几节中，我将详细讨论上述特性。 </P>
<P><A name=N100A4><SPAN class=atitle2>看呀，没有 javac！</SPAN></A><BR>像许多脚本语言一样，Groovy 不用为运行时编译。这意味着 Groovy 脚本是 <I xmlns:dw="http://www.ibm.com/developerworks/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">在它们运行时</I>解释的，就像 JavaScript 是在观看 Web 页时由浏览器解释的一样。运行时判断会有执行速度的代价，这有可能使脚本语言不能用于对性能有要求的项目，但是无编译的代码在构建-运行周期中可以提供很多好处。运行时编译使 Groovy 成为快速原型设计、构建不同的实用程序和测试框架的理想平台。 </P>
<P>
<TABLE cellSpacing=0 cellPadding=5 width="40%" align=right border=1>
<TBODY>
<TR>
<TD background=/developerworks/cn/i/bg-gold.gif>
<P><A name=N100B3><B>脚本的能力</B></A><BR>脚本语言很流行，因为它们容易学习并且为开发人员设置的限制较少。脚本语言通常使用简单的、相当简洁的语法，这使开发人员可以用比大多数编程语言所需要的更少的代码创建真实世界的应用程序。像 Perl、Python、Ruby 和现在的 Groovy，用其敏捷方式编写代码而使编程工作达到一个新的效率水平。这种提高的敏捷性通常会使开发人员的效率提高。脚本语言的成功表明脚本不是一种小范围内使用的技术或者黑客的娱乐工具，而是一种由像 Google、Yahoo 和 IBM 这样的世界级公司所使用的切实可行的技术。</P></TD></TR></TBODY></TABLE></P>
<P>例如，运行脚本 Emailer.groovyin Groovy 就是在命令行键入 <CODE>groovy Emailer.groovy</CODE> 这么容易。如果希望运行同样的 Java 文件（Emailer.java），显然必须键入额外的命令： <CODE>javac Emailer.java</CODE> ，然后是 <CODE>java Emailer</CODE> 。虽然这看起来可能有些微不足道，但是可以容易设想运行时编译在应用程序开发的更大上下文中的好处。 </P>
<P>可以在稍后看到，Groovy 还允许脚本放弃 main 方法以静态地运行一个关联的应用程序。</P>
<P><A name=N100CF><SPAN class=atitle2>动态 dynamo</SPAN></A><BR>像其他主流脚本语言一样，Groovy 不需要像 C++ 和 Java 语言这样的正式语言的显式类型。在 Groovy 中，一个对象的类型是在运行时动态发现的，这极大地减少了要编写的代码数量。首先可以通过分析清单 1 和 2 中的简单例子看到这一点。</P>
<P>清单 1 显示了在 Java 语言中如何将一个本地变量声明为 <CODE>String</CODE> 。注意必须声明类型、名和值。 </P><A name=code1><B>清单 1. Java 静态类型</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
String myStr = "Hello World";
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>在清单 2 中，您看到同样的声明，但是不需要声明变量类型。 </P><A name=code2><B>清单 2. Groovy 动态类型</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
myStr = "Hello World"
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>您可能还注意到了，在清单 2 中我可以去掉声明中的分号。在定义方法及其相关的参数时动态类型有戏剧性的后果：多态具有了全新的意义！事实上，使用动态类型， <I xmlns:dw="http://www.ibm.com/developerworks/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">不使用</I>继承就可以得到多态的全部能力。在清单 3 中，可以真正开始看到动态类型在 Groovy 的灵活性方面所起的作用。 </P><A name=code3><B>清单 3. 更多 Groovy 动态类型</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
class Song{
  length
  name
}

class Book{
  name
  author
}

def doSomething(thing){
  println "going to do something with a thing named = " + thing.name
}
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>这里，我定义了两个 Groovy 类， <CODE>Song</CODE> 和 <CODE>Book</CODE> ，我将在后面对它们进一步讨论。这两个类都包含一个 <CODE>name</CODE> 属性。我还定义了一个函数 <CODE>doSomething</CODE> ，它以一个 <CODE>thing</CODE> 为参数，并试图打印这个对象的 <CODE>name</CODE> 属性。 </P>
<P>因为 <CODE>doSomething</CODE> 函数没有定义其输入参数的类型，只要对象包含一个 <CODE>name</CODE> 属性，那么它就可以工作。因此，在清单 4 中，可以看到在使用 <CODE>Song</CODE> 和 <CODE>Book</CODE> 的实例作为 <CODE>doSomething</CODE> 的输入时会有什么现象。 </P><A name=code4><B>清单 4. 试验动态类型</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
mySong = new Song(length:90, name:"Burning Down the House")
myBook = new Book(name:"One Duck Stuck", author:"Phyllis Root")

doSomething(mySong) //prints Burning Down the House
doSomething(myBook) //prints One Duck Stuck

anotherSomething = doSomething

anotherSomething(myBook) //prints One Duck Stuck
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>除了展示 Groovy 中的动态类型，清单 4 的最后两行还揭示了创建对一个函数的引用有多容易。这是因为在 Groovy 中 <I xmlns:dw="http://www.ibm.com/developerworks/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">所有东西</I>都是对象，包括函数。 </P>
<P>关于 Groovy 的动态类型声明最后要注意的是，它会导致更少的 <CODE>import</CODE> 语句。尽管 Groovy 需要 import 以显式使用类型，但是这些 import 可以使用别名以提供更短的名字。 </P>
<P><A name=N1014F><SPAN class=atitle3>动态类型综述</SPAN></A><BR>下面两个例子将到目前为止讨论过的 Groovy 中的动态类型的内容放到一起。下面的 Java 代码组和 Groovy 代码组利用了 Freemarker（参阅 <A href="http://www-128.ibm.com/developerworks/cn/java/j-alj08034/index.html#resources" xmlns:dw="http://www.ibm.com/developerworks/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">参考资料</A>），这是一个开放源代码模板引擎。这两组代码都只是简单地用一个目录和文件名创建一个 <CODE>Template</CODE> 对象，然后将相应对象的内容打印到标准输出，当然，不同之处是每一组代码处理这项任务所需要的代码量。 </P><A name=code5><B>清单 5. 简单的 TemplateReader Java 类</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
import java.io.File;
import java.io.IOException;

import freemarker.template.Configuration;
import freemarker.template.Template;

public class TemplateReader {

  public static void main(String[] args) {
    try{
	  Configuration cfg = Configuration.getDefaultConfiguration();
	  cfg.setDirectoryForTemplateLoading(
	       new File("C:\\dev\\projects\\http-tester\\src\\conf"));
		    
	  Template temp = cfg.getTemplate("vendor-request.tmpl");
		
  	  System.out.println(temp.toString());
      }catch(IOException e){
        e.printStackTrace();
      }
  }
}
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>初看之下，清单 5 中的 Java 代码相当简单 —— 特别是如果以前从来没见过脚本代码时。幸运的是，有清单 6 中的 Groovy 作为对比。现在这段代码很简单！</P><A name=code6><B>清单 6. 用 Groovy 编写的更简单的 TemplateReader</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
import freemarker.template.Configuration as tconf
import java.io.File

cfg = tconf.getDefaultConfiguration()

cfg.setDirectoryForTemplateLoading(
  new File("C:\\dev\\projects\\http-tester\\src\\conf"))
  
temp = cfg.getTemplate("vendor-request.tmpl")

println temp.toString()
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>Groovy 代码只有 Java 代码的一半那么长，下面是原因：</P>
<UL xmlns:dw="http://www.ibm.com/developerworks/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<LI>Groovy 代码只需要一半的 <CODE>import</CODE> 语句。还要注意， <CODE>freemarker.template.Configuration</CODE> 使用了别名 <CODE>tconf</CODE> ，使得语法更短。 <BR><BR></LI>
<LI>Groovy 允许类型为 <CODE>Template</CODE> 的变量 <CODE>tmpl</CODE> 不声明其类型。 <BR><BR></LI>
<LI>Groovy 不需要 <CODE>class</CODE> 声明或者 <CODE>main</CODE> 方法。 <BR><BR></LI>
<LI>Groovy 不关心任何相应异常，使您可以不用导入 Java 代码中需要的 <CODE>IOException</CODE> 。 </LI></UL>
<P>现在，在继续之前，想一下您所编写的最后一个 Java 类。您可能不得不编写很多 import 并声明类型，并在后面加上同样数量的分号。考虑用 Groovy 编写同样的代码会是什么情况。可以使用简练得多的语法，不需要遵守这么多的规则，并且得到完全相同的行为。</P>
<P>想一下，如果您正好是刚刚开始……</P>
<P><A name=N101BB><SPAN class=atitle2>特别灵活的语法</SPAN></A><BR>谈到语法，灵活性是更有效地开发代码的主要因素。很像其有影响的对手（Python、Ruby 和 Smalltalk），Groovy 极大地简化了核心库的使用和它所模拟的语言（在这里是 Java 语言）的构造。为了让您对 Groovy 语法的灵活性有一个大体概念，我将展示它的一些主要结构，即类、函数（通过 <CODE>def</CODE> 关键词）、闭包、集合、范围、映射和迭代器。 </P>
<P><A name=N101C8><SPAN class=atitle3>类</SPAN></A><BR>在字节码水平，Groovy 类是真正的 Java 类。不同之处在于 Groovy 将类中定义的所有内容都默认为 <CODE>public</CODE> ，除非定义了特定的访问修饰符。而且，动态类型应用到字段和方法，不需要 <CODE>return</CODE> 语句。 </P>
<P>在清单 7 中可以看到 Groovy 中类定义的例子，其中类 <CODE>Dog</CODE> 有一个 <CODE>getFullName</CODE> 方法，它实际上返回一个表示 <CODE>Dog</CODE> 的全名的 <CODE>String</CODE> 。并且所有方法都隐式地为 <CODE>public</CODE> 。 </P><A name=code7><B>清单 7. 示例 Groovy 类：Dog</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
class Dog{
  name

  bark(){
    println "RUFF! RUFF!"
  }
  
  getFullName(master){
    name + " " + master.lname
  }
  
  obeyMaster(){
    println "I hear you and will not obey."
  }
}
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>在清单 8 中，推广到有两个属性 —— <CODE>fname</CODE> 和 <CODE>lname</CODE> —— 的类 <CODE>DogOwner</CODE> ，就是这么简单！ </P><A name=code8><B>清单 8. 示例 Groovy 类：DogOwner</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
class DogOwner{
  fname
  lname

  trainPet(pet){
    pet.obeyMaster()
  }
  
}
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>在清单 9 中，用 Groovy 设置属性并对 <CODE>Dog</CODE> 和 <CODE>DogOwner</CODE> 实例调用方法。现在很明显，使用 Groovy 类比 Java 类要容易得多。虽然需要 <CODE>new</CODE> 关键词，但是类型是可选的，且设置属性（它隐式为 public）是相当轻松的。 </P><A name=code9><B>清单 9. 使用 Groovy 类</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>

myDog = new Dog()
myDog.name = "Mollie"

myDog.bark()
myDog.obeyMaster() 

me = new DogOwner()
me.fname = "Ralf"
me.lname = "Waldo"

me.trainPet(myDog)

str = myDog.getFullName(me)
println str  // prints Mollie Waldo

</CODE></PRE></TD></TR></TBODY></TABLE>
<P>注意在 <CODE>Dog</CODE> 类中定义的 <CODE>getFullName</CODE> 方法返回一个 <CODE>String</CODE> 对象，在这里它是 “ <CODE>Mollie Waldo</CODE> ”。 </P>
<P>
<TABLE cellSpacing=0 cellPadding=5 width="40%" align=right border=1>
<TBODY>
<TR>
<TD background=/developerworks/cn/i/bg-gold.gif>
<P><A name=N10242><B>第一类对象</B></A><BR><I xmlns:dw="http://www.ibm.com/developerworks/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">第一类对象</I> 是可以在运行时用数据创建并使用的对象。第一类对象还可以传递给函数和由函数输出、作为变量存储、由其他对象返回。Java 语言自带的基本数据类型，如 <CODE>int</CODE> 和 <CODE>boolean</CODE> ，不认为是第一类对象。许多面向对象纯粹论者哀叹这个小细节，一些人据此提出 Java 语言是否是真正的面向对象语言。Groovy 通过将所有内容声明为对象而解决了这一问题。 </P></TD></TR></TBODY></TABLE></P>
<P><A name=N10257><SPAN class=atitle3>Def</SPAN></A><BR>除了像许多脚本语言那样将所有对象指定为第一类对象（见侧栏），Groovy 还让您创建 <I xmlns:dw="http://www.ibm.com/developerworks/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">第一类函数</I>，它本身实质上就是对象。它们是用 <CODE>def</CODE> 关键词定义的并在类定义之外。您实际上在 <A href="http://www-128.ibm.com/developerworks/cn/java/j-alj08034/index.html#code3" xmlns:dw="http://www.ibm.com/developerworks/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">清单 3</A> 中已经看到了如何用 <CODE>def</CODE> 关键词定义第一类函数，并在 <A href="http://www-128.ibm.com/developerworks/cn/java/j-alj08034/index.html#code4" xmlns:dw="http://www.ibm.com/developerworks/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">清单 4</A>中看到使用了一个函数。Groovy 的第一类函数定义简单脚本时特别有用。 </P>
<P><A name=N10273><SPAN class=atitle3>闭包</SPAN></A><BR>Groovy 中最令人兴奋和最强大的功能是支持闭包。 <I xmlns:dw="http://www.ibm.com/developerworks/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">闭包（Closure）</I>是第一类对象，它类似于 Java 语言中的匿名内部类。闭包和匿名内部类都是可执行的一段代码，不过这两者之间有一些细微的不同。状态是自动传入传出闭包的。闭包可以有名字。它们可以重复使用。而且，最重要且对 Groovy 同样成立的是，闭包远比匿名内部类要灵活得多！ </P>
<P>清单 10 展示了闭包的强大。清单中新的和改进的 <CODE>Dog</CODE> 类包括一个 <CODE>train</CODE> 方法，它实际上执行创建了 <CODE>Dog</CODE> 实例的闭包。 </P><A name=code10><B>清单 10. 使用闭包</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
class Dog{  
  action

  train(){
    action.call()
  }
}

sit = { println "Sit, Sit! Sit! Good dog"}
down = { println "Down! DOWN!" }


myDog = new Dog(action:sit)
myDog.train()  // prints Sit, Sit! Sit! Good dog

mollie = new Dog(action:down)
mollie.train() // prints Down! DOWN!
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>而且，闭包还可以接收参数。如清单 11 所示， <CODE>postRequest</CODE> 闭包接收两个参数（ <CODE>location</CODE> 和 <CODE>xml</CODE> ），并使用 Jakarta Commons HttpClient 库（参阅 <A href="http://www-128.ibm.com/developerworks/cn/java/j-alj08034/index.html#resources" xmlns:dw="http://www.ibm.com/developerworks/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">参考资料</A>）将一个 XML 文档发送给指定位置。然后这个闭包返回一个表示响应的 <CODE>String</CODE> 。闭包定义下面是一个使用闭包的例子。事实上，调用闭包就像调用函数一样。 </P><A name=code11><B>清单 11. 使用带参数的闭包</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
import org.apache.commons.httpclient.HttpClient
import org.apache.commons.httpclient.methods.PostMethod

postRequest = { location, xml |

  clint = new HttpClient()
  mthd = new PostMethod(location)  	
  mthd.setRequestBody(xml)
  mthd.setRequestContentLength(xml.length())
  mthd.setRequestHeader("Content-type", 
     "text/xml; charset=ISO-8859-1")
	
  statusCode = clint.executeMethod(mthd)
  responseBody = mthd.getResponseBody()
  mthd.releaseConnection()	
  return new String(responseBody)	  
}

loc = "http://localhost:8080/simulator/AcceptServlet/"
vxml = "&lt;test&gt;&lt;data&gt;blah blah blah&lt;/data&gt;&lt;/test&gt;"

str = postRequest(loc, vxml)
println str
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>
<TABLE cellSpacing=0 cellPadding=5 width="40%" align=right border=1>
<TBODY>
<TR>
<TD background=/developerworks/cn/i/bg-gold.gif>
<P><A name=N102BC><B>自动装箱</B></A><BR>自动装箱或者装箱转换是一个自动将像 <CODE>int</CODE> 、 <CODE>double</CODE> 和 <CODE>boolean</CODE> 这样的基本数据类型自动转换为可以在 <CODE>java.lang</CODE> 包中找到的它们的相应包装类型的过程。这一功能出现在 J2SE 1.5 中，使开发人员不必在源代码中手工编写转换代码。 </P></TD></TR></TBODY></TABLE></P>
<P><A name=N102D6><SPAN class=atitle3>集合</SPAN></A><BR>将对象组织到像列表和映射这样的数据结构中是一项基本的编码任务，是我们大多数人每天要做的工作。像大多数语言一样，Groovy 定义了一个丰富的库以管理这些类型的集合。如果曾经涉足 Python 或者 Ruby，那么应该熟悉 Groovy 的集合语法。如清单 12 所示，创建一个列表与在 Java 语言中创建一个数组很类似。（注意，列表的第二项自动装箱为一个 <CODE>Integer</CODE> 类型。) </P><A name=code12><B>清单 12. 使用集合</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
collect = ['groovy', 29, 'here', 'groovy']
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>除了使列表更容易处理，Groovy 还为集合增加了几个新方法。这些方法使得，如 <CODE>统计</CODE> 值出现的次数、将整个列表 <CODE>结合</CODE> 到一起、对列表 <CODE>排序</CODE> 变得非常容易。可以在清单 13 中看到这些集合方法的使用。 </P><A name=code13><B>清单 13. 使用 Groovy 集合</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
aCollect = [5, 9, 2, 2, 4, 5, 6] 

println aCollect.join(' - ')  // prints 5 - 9 - 2 - 2 - 4 - 5 - 6
println aCollect.count(2)     // prints 2
println aCollect.sort()       // prints [2, 2, 4, 5, 5, 6, 9]
</CODE></PRE></TD></TR></TBODY></TABLE>
<P><B xmlns:dw="http://www.ibm.com/developerworks/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">Maps</B> <BR xmlns:dw="http://www.ibm.com/developerworks/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><BR xmlns:dw="http://www.ibm.com/developerworks/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">像列表一样，映射也是一种在 Groovy 中非常容易处理的数据结构。清单 14 中的映射包含两个对象，键是 <CODE>name</CODE> 和 <CODE>date</CODE> 。注意可以用不同的方式取得值。 </P><A name=code14><B>清单 14. 处理映射</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
myMap = ["name" : "Groovy", "date" : new Date()]

println myMap["date"]

println myMap.date
</CODE></PRE></TD></TR></TBODY></TABLE>
<P><B xmlns:dw="http://www.ibm.com/developerworks/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">范围</B> <BR xmlns:dw="http://www.ibm.com/developerworks/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><BR xmlns:dw="http://www.ibm.com/developerworks/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">在处理集合时，很可能会大量使用 <CODE>范围（Range）</CODE> 。 <CODE>范围</CODE> 实际上是一个很直观的概念，并且容易理解，利用它可以包含地或者排除地创建一组有序值。使用两个点 （ <CODE>..</CODE> ） 声明一个包含范围，用三个点 （ <CODE>...</CODE> ） 声明一个排除范围，如清单 15 所示。 </P><A name=code15><B>清单 15. 处理范围</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
myRange = 29...32
myInclusiveRange = 2..5

println myRange.size() // prints 3
println myRange[0]   // prints 29
println myRange.contains(32) //prints false

println myInclusiveRange.contains(5) //prints true
</CODE></PRE></TD></TR></TBODY></TABLE>
<P><B xmlns:dw="http://www.ibm.com/developerworks/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">用范围实现循环</B> <BR xmlns:dw="http://www.ibm.com/developerworks/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><BR xmlns:dw="http://www.ibm.com/developerworks/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">在循环结构中，范围可以实现相当巧妙的想法。在清单 16 中，将 <CODE>aRange</CODE> 定义为一个排除范围，循环打印 a、b、c 和 d。 </P><A name=code16><B>清单 16. 用范围实现循环</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
aRange = 'a'...'e'

for (i in aRange){
  println i
}
</CODE></PRE></TD></TR></TBODY></TABLE>
<P><B xmlns:dw="http://www.ibm.com/developerworks/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">集合的其他功能</B> <BR xmlns:dw="http://www.ibm.com/developerworks/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><BR xmlns:dw="http://www.ibm.com/developerworks/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">如果不熟悉 Python 和其他脚本语言，那么您在 Groovy 集合中发现的一些其他功能会让您印象深刻。例如，创建了集合后，可以用负数在列表中反向计数，如清单 17 所示。 </P><A name=code17><B>清单 17. 负索引</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
aList = ['python', 'ruby', 'groovy']

println aList[-1] // prints groovy
println aList[-3] // prints python
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>Groovy 还让您可以用范围分割列表。分割可获得列表的准确子集，如清单 18 所示。 </P><A name=code18><B>清单 18. 用范围分割</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
fullName = "Andrew James Glover"

mName = fullName[7...13]

print "middle name: " + mName // prints James
</CODE></PRE></TD></TR></TBODY></TABLE>
<P><B xmlns:dw="http://www.ibm.com/developerworks/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">集合类似于 Ruby</B> <BR xmlns:dw="http://www.ibm.com/developerworks/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><BR xmlns:dw="http://www.ibm.com/developerworks/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">如果愿意的话，还可以将 Groovy 集合作为 Ruby 集合。可以用类似 Ruby 的语法，以 <CODE>&lt;&lt;</CODE> 语法附加元素、用 <CODE>+</CODE> 串接和用 <CODE>-</CODE> 对集合取差，甚至还可以用 <CODE>*</CODE> 语法处理集合的重复，如清单 19 所示。注意，还可以用 <CODE>==</CODE> 比较集合。 </P><A name=code19><B>清单 19. Ruby 风格的集合</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
collec = [1, 2, 3, 4, 5]
collec &lt;&lt; 6 //appended 6 to collec

acol = ['a','b','c'] * 3 //acol now has 9 elements

coll =  [10, 11]
coll2 = [12, 13]

coll3 = coll + coll2 //10,11,12,13

difCol = [1,2,3] - [1,2] //difCol is 3

assert [1, 2, 3] == [1, 2, 3] //true
</CODE></PRE></TD></TR></TBODY></TABLE>
<P><A name=N103A7><SPAN class=atitle3>迭代器</SPAN></A><BR>在 Groovy 中，迭代任何序列都相当容易。迭代字符序列所需要的就是一个简单的 <CODE>for</CODE> 循环，如清单 20 所示。（正如您现在可能注意到的，Groovy 提供了比 Java 1.5 以前的传统语法更自然的 <CODE>for</CODE> 循环语法。） </P><A name=code20><B>清单 20. 迭代器示例</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
str = "uncle man, uncle man"

for (ch in str){
  println ch
}
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>Groovy 中的大多数对象具有像 <CODE>each</CODE> 和 <CODE>find</CODE> 这样的以闭包为参数的方法。用闭包来迭代对象会产生几种令人兴奋的可能性，如清单 21 所示。 </P><A name=code21><B>清单 21. 带有迭代器的闭包</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
[1, 2, 3].each {  
  val = it 
  val += val
  println val
}

[2, 4, 6, 8, 3].find { x |
     if (x == 3){
       println x
     }
}
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>在清单 21 中，方法 <CODE>each</CODE> 作为迭代器。在这里，闭包添加元素的值，完成时 <CODE>val</CODE> 的值为 6。 <CODE>find</CODE> 方法也是相当简单的。每一次迭代传递进元素。在这里，只是测试值是否为 3。 </P>
<P><A name=N103E6><SPAN class=atitle2>Groovy 的高级内容</SPAN></A><BR>到目前为止，我着重讲述的都是使用 Groovy 的基本方面，但是这种语言有比基本内容多得多的内容！我将以分析 Groovy 提供的一些高级开发功能作为结束，包括 Groovy 样式的 JavaBeans 组件、文件 IO、正则表达式和用 <CODE>groovyc</CODE> 编译。 </P>
<P><A name=N103F3><SPAN class=atitle3>GroovyBean！</SPAN></A><BR>永远不变的是，应用程序最后要使用类似 struct 的对象表示真实世界的实体。在 Java 平台上，称这些对象为 JavaBean 组件，它们通常用于表示订单、客户、资源等的业务对象。Groovy 由于其方便的简写语法，以及在定义了所需 bean 的属性后自动提供构造函数，而简化了 JavaBean 组件的编写。结果自然就是极大地减少了代码，正如您可以自己从清单 22 和 23 中看到的。</P>
<P>在清单 22 中，可看到一个简单的 JavaBean 组件，它是用 Java 语言定义的。</P><A name=code22><B>清单 22. 一个简单的 JavaBean 组件</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
public class LavaLamp {
  private Long model;
  private String baseColor;
  private String liquidColor;
  private String lavaColor;

  public String getBaseColor() {
    return baseColor;
  }

  public void setBaseColor(String baseColor) {
    this.baseColor = baseColor;
  }

  public String getLavaColor() {
    return lavaColor;
  }

  public void setLavaColor(String lavaColor) {
    this.lavaColor = lavaColor;
  }

  public String getLiquidColor() {
    return liquidColor;
  }

  public void setLiquidColor(String liquidColor) {
    this.liquidColor = liquidColor;
  }

  public Long getModel() {
    return model;
  }

  public void setModel(Long model) {
    this.model = model;
  }
}
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>在清单 23 中，可以看到用 Groovy 写这个 bean 时所发生的事。所要做的就是定义属性，Groovy 会自动给您一个很好的构造函数以供使用。Groovy 还使您在操纵 <CODE>LavaLamp</CODE> 的实例时有相当大的灵活性。例如，我们可以使用 Groovy 的简写语法 <I xmlns:dw="http://www.ibm.com/developerworks/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">或者</I>传统的冗长的 Java 语言语法操纵 bean 的属性。 </P><A name=code23><B>清单 23. 用 Groovy 编写的 JavaBeans 组件</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
class LavaLamp{
  model
  baseColor
  liquidColor
  lavaColor
}

llamp = new LavaLamp(model:1341, baseColor:"Black", 
  liquidColor:"Clear", lavaColor:"Green")

println llamp.baseColor
println "Lava Lamp model ${llamp.model}"

myLamp = new LavaLamp()
myLamp.baseColor = "Silver"
myLamp.setLavaColor("Red")

println "My Lamp has a ${myLamp.baseColor} base"
println "My Lava is " + myLamp.getLavaColor()
</CODE></PRE></TD></TR></TBODY></TABLE>
<P><A name=N1041D><SPAN class=atitle3>轻松的 IO</SPAN></A><BR>Groovy IO 操作很轻松，特别是与迭代器和闭包结合时。Groovy 使用标准 Java 对象如 <CODE>File</CODE> 、 <CODE>Reader</CODE> 和 <CODE>Writer</CODE> ，并用接收闭包作参数的额外方法增强了它们。如在清单 24 中，可以看到传统的 <CODE>java.io.File</CODE> ，但是带有额外的、方便的 <CODE>eachLine</CODE> 方法。 </P><A name=code24><B>清单 24. Groovy IO</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
import java.io.File

new File("File-IO-Example.txt").eachLine{ line |
 println "read the following line -&gt; " + line
}
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>因为文件实质上是一系列行、字符等，所以可以相当简单地迭代它们。 <CODE>eachLine</CODE> 方法接收一个闭包并迭代文件的每一行，在这里是 <CODE>File-IO-Example.txt</CODE> 。 以这种方式使用闭包是相当强大的，因为 Groovy 保证所有文件资源都是关闭的，不考虑任何异常。这意味着无需大量 <CODE>try</CODE> / <CODE>catch</CODE> / <CODE>finally</CODE> 子句就可以进行文件 IO！ </P>
<P><A name=N1045B><SPAN class=atitle3>高级编译</SPAN></A><BR>Groovy 脚本实际上是字节码级别的 Java 类。因此，可以容易地用 <CODE>groovyc</CODE> 编译 Groovy 脚本。可以通过命令行或者 <CODE>Ant</CODE> 使用 <CODE>groovyc</CODE> 以生成脚本的类文件。这些类可以用普通 <CODE>java</CODE> 命令运行，只要 classpath 包括 <CODE>groovy.jar</CODE> 和 <CODE>asm.jar</CODE> ，这是 ObjectWeb 的字节码操纵框架。要了解更多编译 Groovy 的内容，请参阅 <A href="http://www-128.ibm.com/developerworks/cn/java/j-alj08034/index.html#resources" xmlns:dw="http://www.ibm.com/developerworks/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">参考资料</A>。 </P>
<P><A name=N10480><SPAN class=atitle3>最大 RegEx</SPAN></A><BR>如果一种语言没有正则表达式处理，则它是没价值的。Groovy 使用 Java 平台的 <CODE>java.util.regex</CODE> 库 —— 但是做了少量基本的改变。例如，Groovy 使您可以用 <CODE>~</CODE> 表达式创建 <CODE>Pattern</CODE> 对象，用 <CODE>=~</CODE> 表达式创建 <CODE>Matcher</CODE> 对象，如清单 25 所示。 </P><A name=code25><B>清单 25. Groovy RegEx</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
str =  "Water, water, every where,
        And all the boards did shrink;
        Water, water, every where,
        Nor any drop to drink."

if (str =~ 'water'){
  println 'found a match'
}

ptrn = ~"every where"

nStr = (str =~ 'every where').replaceAll('nowhere')

println nStr
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>您可能已经注意到了，可以在上述清单中定义 <CODE>String</CODE> 、 <CODE>str</CODE> ，而无需为每一新行添加结束引号和 <CODE>+</CODE> 。这是因为 Groovy 放松了要求字符串串接的普通 Java 约束。运行这段 Groovy 脚本会对匹配 <CODE>water</CODE> 的情况打印出 <CODE>true</CODE> ，然后打印出一节诗，其中所有出现 “ <CODE>every where</CODE> ”的地方都替换为 “ <CODE>nowhere</CODE> ”。 </P>
<P>
<TABLE cellSpacing=0 cellPadding=5 width="40%" align=right border=1>
<TBODY>
<TR>
<TD background=/developerworks/cn/i/bg-gold.gif>
<P><A name=N104C9><B>关于 （band）shell</B></A><BR>Groovy 提供了两种不同的解释器，使所有有效的 Groovy 表达式可以交互地执行。这些 shell 是特别强大的机制，可以用它们迅速学习 Groovy。</P></TD></TR></TBODY></TABLE></P>
<P><A name=N104D3><SPAN class=atitle2>结束语</SPAN></A><BR>像所有婴儿期的项目一样，Groovy 是正在发展的语言。习惯于使用 Ruby 和 Python （或者 Jython）的开发人员可能会怀念 mixins、脚本导入（尽管可以将所需要的可导入脚本编译为相应的 Java 类）和方法调用的命名参数等这些功能的方便性。 但是 Groovy 绝对是一种发展中的语言。随着其开发人员数量的增加，它很有可能结合这些功能及更多功能。</P>
<P>同时， Groovy 有很多优点。它很好地融合了 Ruby、Python 和 Smalltalk 的一些最有用的功能，同时保留了基于 Java 语言的核心语法。对于熟悉 Java 平台的开发人员，Groovy 提供了更简单的替代语言，且几乎不需要学习时间。对于刚接触 Java 平台的开发人员，它可以作为有更复杂语法和要求的 Java 语言的一个容易的入口点。</P>
<P>像在本系统讨论的其他语言一样，Groovy 不是要替代 Java 语言，而是作为它的另一种选择。与这里讨论的其他语言不一样，Groovy 遵循 Java 规范，这意味着它有可能与 Java 平台上的 Java 语言具有同等重要的作用。</P>
<P>在本月这一期 <I xmlns:dw="http://www.ibm.com/developerworks/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">alt.lang.jre</I>文章中，介绍了 Groovy 的基本框架和语法，以及它的一些高级编程功能。下个月将介绍在 Java 开发人员中最受欢迎的脚本语言： JRuby。 </P><img src ="http://www.blogjava.net/mstar/aggbug/5714.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/mstar/" target="_blank">黑灵</a> 2005-06-08 10:19 <a href="http://www.blogjava.net/mstar/archive/2005/06/08/5714.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>