﻿<?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-学历代表过去，能力代表现在，学习力代表未来！-随笔分类-groovy</title><link>http://www.blogjava.net/ljc-java/category/49290.html</link><description>模仿中成长，在创新中成功！</description><language>zh-cn</language><lastBuildDate>Thu, 04 Aug 2011 07:21:25 GMT</lastBuildDate><pubDate>Thu, 04 Aug 2011 07:21:25 GMT</pubDate><ttl>60</ttl><item><title>实战 Groovy: for each 剖析(转载)</title><link>http://www.blogjava.net/ljc-java/archive/2011/08/04/355737.html</link><dc:creator>小罗</dc:creator><author>小罗</author><pubDate>Thu, 04 Aug 2011 03:50:00 GMT</pubDate><guid>http://www.blogjava.net/ljc-java/archive/2011/08/04/355737.html</guid><wfw:comment>http://www.blogjava.net/ljc-java/comments/355737.html</wfw:comment><comments>http://www.blogjava.net/ljc-java/archive/2011/08/04/355737.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/ljc-java/comments/commentRss/355737.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/ljc-java/services/trackbacks/355737.html</trackback:ping><description><![CDATA[<div><p> 迭代是编程的基础。您经常会遇到需要进行逐项遍历的内容，比如 <code>List</code>、<code>File</code> 和 JDBC <code>ResultSet</code>。Java 语言几乎总是提供了某种方法帮助您逐项遍历所需的内容，但令人沮丧的是，它并没有给出一种标准方法。Groovy 的迭代方法非常实用，在这一点上，Groovy 编程与 Java 编程截然不同。通过一些代码示例，本文将介绍 Groovy 的万能的 <code>each()</code> 方法，从而将 Java 语言的那些迭代怪癖抛在脑后。 </p> 			<p><a name="N10091">Java 迭代策略</a></p> 			<p> 假设您有一个 Java 编程语言的 <code>java.util.List</code>。清单 1 展示了在 Java 语言中如何使用编程实现迭代： </p> 			<br /><a name="listing1"><strong>清单 1. Java 列表迭代</strong></a><br /><table border="0" cellpadding="0" cellspacing="0" height="87" width="3229"><tbody><tr><td><pre>import java.util.*;  <br />public class ListTest{<br />&nbsp;public static void main(String[] args){<br />&nbsp;List&lt;String&gt; list = new ArrayList&lt;String&gt;();<br />&nbsp;list.add("Java");<br />&nbsp;list.add("Groovy");<br />&nbsp;list.add("JavaScript");<br />&nbsp;for(Iterator&lt;String&gt; i = list.iterator(); i.hasNext();){<br />&nbsp;String language = i.next();<br />&nbsp;System.out.println("I know " + language);<br />&nbsp;}<br />&nbsp;}<br />&nbsp;} </pre></td></tr></tbody></table><br /> 			<p> 由于提供了大部分集合类都可以共享的 <code>java.lang.Iterable</code> 接口，您可以使用相同的方法遍历 <code>java.util.Set</code> 或 <code>java.util.Queue</code>。 </p> 			<div ibm-alt-header="" dw-container-sidebar=""><h2>关于本系列</h2><div> 				 				<p>Groovy 是一款运行在 Java 平台之上的现代编程语言。它能够与现有 Java 代码无缝集成，同时引入了闭包和元编程等出色的新特性。简而言之，Groovy 类似于 21 世纪的 Java 语言。 </p> 				<p>如果要将新工具集成到开发工具箱中，最关键的是理解什么时候需要使用它以及什么时候不适合使用它。Groovy 可以变得非常强大，但前提是它被适当地应用到合适的场景中。因此，<a href="http://www.ibm.com/developerworks/cn/java/j-pg/"> 						<em>实战 Groovy</em> 					</a> 系列旨在展示 Groovy 的实际使用，以及何时和如何成功应用它。 </p> 			</div></div> 			<p> 现在，假设该语言存储在 <code>java.util.Map</code> 中。在编译时，尝试对 <code>Map</code> 获取 <code>Iterator</code> 会导致失败 &#8212; 				<code> Map</code> 并没有实现 <code>Iterable</code> 接口。幸运的是，可以调用 <code>map.keySet()</code> 返回一个 <code>Set</code>，然后就可以继续处理。这些小差异可能会影响您的速度，但不会妨碍您的前进。需要注意的是，<code>List</code>、<code>Set</code> 和 <code>Queue</code> 实现了 <code>Iterable</code>，但是 <code>Map</code> 没有 &#8212; 即使它们位于相同的 <code>java.util</code> 包中。 </p> 			<p> 现在假设该语言存在于 <code>String</code> 数组中。数组是一种数据结构，而不是类。不能对 <code>String</code> 数组调用 <code>.iterator()</code>，因此必须使用稍微不同的迭代策略。您再一次受到阻碍，但可以使用如清单 2 所示的方法解决问题： </p> 			<br /><a name="listing2"><strong>清单 2. Java 数组迭代</strong></a><br /><table border="0" cellpadding="0" cellspacing="0" height="182" width="416"><tbody><tr><td><pre>public class ArrayTest{<br />&nbsp;public static void main(String[] args){<br />&nbsp;String[] list = {"Java", "Groovy", "JavaScript"};<br />&nbsp;for(int i = 0; i &lt; list.length; i++){<br />&nbsp;String language = list[i];<br />&nbsp;System.out.println("I know " + language);<br />&nbsp;}<br />&nbsp;}<br />&nbsp;} </pre></td></tr></tbody></table><br /> 			<p> 但是等一下 &#8212; 使用 Java 5 引入的 for-each 语法怎么样？它可以处理任何实现 <code>Iterable</code> 的类和数组，如清单 3 所示： </p> 			<br /><a name="listing3"><strong>清单 3. Java 语言的 for-each 迭代</strong></a><br /><table border="0" cellpadding="0" cellspacing="0" height="308" width="443"><tbody><tr><td><pre>import java.util.*;<br />&nbsp;public class MixedTest{<br />&nbsp;public static void main(String[] args){<br />&nbsp;List&lt;String&gt; list = new ArrayList&lt;String&gt;();<br />&nbsp;list.add("Java");<br />&nbsp;list.add("Groovy");<br />&nbsp;list.add("JavaScript");<br />&nbsp;for(String language: list){<br />&nbsp;System.out.println("I know " + language);<br />&nbsp;}      <br />String[] list2 = {"Java", "Groovy", "JavaScript"};<br />&nbsp;for(String language: list2){<br />&nbsp;System.out.println("I know " + language);<br />&nbsp;}<br />&nbsp;}<br />&nbsp;} </pre></td></tr></tbody></table><br /> 			<p> 因此，您可以使用相同的方法遍历数组和集合（<code>Map</code> 除外）。但是如果语言存储在 <code>java.io.File</code>，那该怎么办？如果存储在 JDBC <code>ResultSet</code>，或者存储在 XML 文档、<code>java.util.StringTokenizer</code> 中呢？面对每一种情况，必须使用一种稍有不同的迭代策略。这样做并不是有什么特殊目的 &#8212; 而是因为不同的 API 是由不同的开发人员在不同的时期开发的 &#8212; 但事实是，您必须了解 6 个 Java 迭代策略，特别是使用这些策略的特殊情况。  </p> 			<p> Eric S. Raymond 在他的 <em>The Art of Unix Programming</em> 一书中解释了  &#8220;最少意外原则&#8221;。他写道，&#8220;要设计可用的接口，最好不要设计全新的接口模型。新鲜的东西总是难以入门；会为用户带来学习的负担，因此应当尽量减少新内 容。&#8221;Groovy 对迭代的态度正是采纳了 Raymond 的观点。在 Groovy 中遍历几乎任何结构时，您只需要使用 <code>each()</code> 这一种方法。 </p> 			<p ibm-back-to-top=""><a href="http://www.ibm.com/developerworks/cn/java/j-pg04149.html#ibm-pcon"><br /></a></p><p><a name="N1015E">Groovy 中的列表迭代</a></p> 			<p> 首先，我将 <a href="http://www.ibm.com/developerworks/cn/java/j-pg04149.html#listing3">清单 3</a> 中的 <code>List</code> 重构为 Groovy。在这里，只需要直接对列表调用 <code>each()</code> 方法并传递一个闭包，而不是将 <code>List</code> 转换成 <code>for</code> 循环（顺便提一句，这样做并不是特别具有面向对象的特征，不是吗）。 </p> 			<p> 创建一个名为 listTest.groovy 的文件并添加清单 4 中的代码：</p> 			<br /><a name="listing4"><strong>清单 4. Groovy 列表迭代</strong></a><br /><table border="0" cellpadding="0" cellspacing="0" height="38" width="691"><tbody><tr><td><pre>def list = ["Java", "Groovy", "JavaScript"] list.each{language-&gt;   println language } </pre></td></tr></tbody></table><br /> 			<p> 清单 4 中的第一行是 Groovy 用于构建 <code>java.util.ArrayList</code> 的便捷语法。可以将 <code>println list.class</code> 添加到此脚本来验证这一点。接下来，只需对列表调用 <code>each()</code>，并在闭包体内输出 <code>language</code> 变量。在闭包的开始处使用 <code>language-&gt;</code> 语句命名 <code>language</code> 变量。如果没有提供变量名，Groovy 提供了一个默认名称 <code>it</code>。在命令行提示符中输入 <code>groovy listTest</code> 运行 listTest.groovy。</p> 			<p>清单 5 是经过简化的 <a href="http://www.ibm.com/developerworks/cn/java/j-pg04149.html#listing4">清单 4</a> 代码版本：</p> 			<br /><a name="listing5"><strong>清单 5. 使用 Groovy 的 <code>it</code> 变量的迭代</strong></a><br /><table border="0" cellpadding="0" cellspacing="0" height="110" width="502"><tbody><tr><td><pre> // shorter, using the default it variable <br />def list = ["Java", "Groovy", "JavaScript"]<br />&nbsp;list.each{ println it }<br />&nbsp;// shorter still, using an anonymous <br />list ["Java", "Groovy", "JavaScript"].each{ println it } </pre></td></tr></tbody></table><br /> 			<p> Groovy 允许您对数组和 <code>List</code> 交替使用 <code>each()</code> 方法。为了将 <code>ArrayList</code> 改为 <code>String</code> 数组，必须将 <code>as String[]</code> 添加到行末，如清单 6 所示： </p> 			<br /><a name="listing6"><strong>清单 6. Groovy 数组迭代</strong></a><br /><table border="0" cellpadding="0" cellspacing="0" height="38" width="635"><tbody><tr><td><pre>def list = ["Java", "Groovy", "JavaScript"] as String[] list.each{println it} </pre></td></tr></tbody></table><br /> 			<p> 在 Groovy 中普遍使用 <code>each()</code> 方法，并且 getter 语法非常便捷（<code>getClass()</code> 和 <code>class</code> 是相同的调用），这使您能够编写既简洁又富有表达性的代码。例如，假设您希望利用反射显示给定类的所有公共方法。清单 7 展示了这个例子： </p> 			<br /><a name="listing7"><strong>清单 7. Groovy 反射</strong></a><br /><table border="0" cellpadding="0" cellspacing="0" height="236" width="582"><tbody><tr><td><pre>def s = "Hello World"<br />&nbsp;println s <br />println s.class<br />&nbsp;s.class.methods.each{println it}<br />&nbsp;//output: <br />$ groovy reflectionTest.groovy<br />&nbsp;Hello World<br />&nbsp;class java.lang.String<br />&nbsp;public int java.lang.String.hashCode()<br />&nbsp;public volatile int java.lang.String.compareTo(java.lang.Object)<br />&nbsp;public int java.lang.String.compareTo(java.lang.String)<br />&nbsp;public boolean java.lang.String.equals(java.lang.Object) ... </pre></td></tr></tbody></table><br /> 			<p> 脚本的最后一行调用 <code>getClass()</code> 方法。<code>java.lang.Class</code> 提供了一个 <code>getMethods()</code> 方法，后者返回一个数组。通过将这些操作串连起来并对 <code>Method</code> 的结果数组调用 <code>each()</code>，您只使用了一行代码就完成了大量工作。 </p> 			<p> 但是，与 Java for-each 语句不同的是，万能的 <code>each()</code> 方法并不仅限于 <code>List</code> 和数组。在 Java 语言中，故事到此结束。然而，在 Groovy 中，故事才刚刚开始。 </p> 			<p><a name="N1021C">Map 迭代</a></p> 			<p> 从前文可以看到，在 Java 语言中，无法直接迭代 <code>Map</code>。在 Groovy 中，这完全不是问题，如清单 8 所示： </p> 			<br /><a name="listing8"><strong>清单 8. Groovy map 迭代</strong></a><br /><table border="0" cellpadding="0" cellspacing="0" height="38" width="725"><tbody><tr><td><pre>def map = ["Java":"server", "Groovy":"server", "JavaScript":"web"] map.each{ println it } </pre></td></tr></tbody></table><br /> 			<p> 要处理名称/值对，可以使用隐式的 <code>getKey()</code> 和 <code>getValue()</code> 方法，或在包的开头部分显式地命名变量，如清单 9 所示： </p> 			<br /><a name="listing9"><strong>清单 9. 从 map 获得键和值</strong></a><br /><table border="0" cellpadding="0" cellspacing="0" height="182" width="650"><tbody><tr><td><pre>def map = ["Java":"server", "Groovy":"server", "JavaScript":"web"]<br />&nbsp;map.each{<br />&nbsp;println it.key<br />&nbsp;println it.value<br />&nbsp;}  <br />map.each{k,v-&gt;<br />&nbsp;println k<br />&nbsp;println v<br />&nbsp;} </pre></td></tr></tbody></table><br /> 			<p> 可以看到，迭代 <code>Map</code> 和迭代其它任何集合一样自然。 </p> 			<p> 在继续研究下一个迭代例子前，应当了解 Groovy 中有关 <code>Map</code> 的另一个语法。与在 Java 语言中调用 <code>map.get("Java")</code> 不一样，可以简化对 <code>map.Java</code> 的调用，如清单 10 所示： </p> 			<br /><a name="listing10"><strong>清单 10. 获得 map 值</strong></a><br /><table border="0" cellpadding="0" cellspacing="0" height="92" width="590"><tbody><tr><td><pre>def map = ["Java":"server", "Groovy":"server", "JavaScript":"web"]<br />&nbsp;//identical results<br />&nbsp;println map.get("Java")<br />&nbsp;println map.Java </pre></td></tr></tbody></table><br /> 			<p> 不可否认，Groovy 针对 <code>Map</code> 的这种便捷语法非常酷，但这也是在对 <code>Map</code> 使用反射时引起一些常见问题的原因。对 <code>list.class</code> 的调用将生成 <code>java.util.ArrayList</code>，而调用 <code>map.class</code> 返回 <code>null</code>。这是因为获得 map 元素的便捷方法覆盖了实际的 getter 调用。<code>Map</code> 中的元素都不具有 <code>class</code> 键，因此调用实际会返回 <code>null</code>，如清单 11 的示例所示： </p> 			<br /><a name="listing11"><strong>清单 11. Groovy map 和 <code>null</code> 				</strong></a><br /><table border="0" cellpadding="0" cellspacing="0" height="236" width="485"><tbody><tr><td><pre>def list = ["Java", "Groovy", "JavaScript"]<br />&nbsp;println list.class<br />&nbsp;// java.util.ArrayList<br />&nbsp;def map = ["Java":"server", "Groovy":"server", "JavaScript":"web"]<br />&nbsp;println map.class<br />&nbsp;// null<br />&nbsp;map.class = "I am a map element"<br />&nbsp;println map.class<br />&nbsp;// I am a map element<br />&nbsp;println map.getClass()<br />&nbsp;// class<br />&nbsp;java.util.LinkedHashMap </pre></td></tr></tbody></table><br /> 			<p>这是 Groovy 比较罕见的打破 &#8220;最少意外原则&#8221; 的情况，但是由于从 map 获取元素要比使用反射更加常见，因此我可以接受这一例外。</p> 			<p ibm-back-to-top=""><a href="http://www.ibm.com/developerworks/cn/java/j-pg04149.html#ibm-pcon"><br /></a></p><p><a name="N102A0">String 迭代</a></p> 			<p> 现在您已经熟悉 <code>each()</code> 方法了，它可以出现在所有相关的位置。假设您希望迭代一个 <code>String</code>，并且是逐一迭代字符，那么马上可以使用 <code>each()</code> 方法。如清单 12 所示： </p> 			<br /><a name="listing12"><strong>清单 12. <code>String</code> 迭代</strong></a><br /><table border="0" cellpadding="0" cellspacing="0" height="74" width="446"><tbody><tr><td><pre>def name = "Jane Smith" name.each{letter-&gt;<br />&nbsp;println letter<br />&nbsp;} </pre></td></tr></tbody></table><br /> 			<p> 这提供了所有的可能性，比如使用下划线替代所有空格，如清单 13 所示： </p> 			<br /><a name="listing13"><strong>清单 13. 使用下划线替代空格</strong></a><br /><table border="0" cellpadding="0" cellspacing="0" height="218" width="515"><tbody><tr><td><pre><br />def name = "Jane Smith"<br />&nbsp;println "replace spaces" <br />name.each{<br />&nbsp;if(it == " "){<br />&nbsp;print "_"<br />&nbsp;}else{<br />&nbsp;print it<br />&nbsp;}<br />&nbsp;}<br />&nbsp;// output Jane_Smith </pre></td></tr></tbody></table><br /> 			<p> 当然，在替换一个单个字母时，Groovy 提供了一个更加简洁的替换方法。您可以将清单 13 中的所有代码合并为一行代码：<code>"Jane Smith".replace(" ", "_")</code>。但是对于更复杂的 <code>String</code> 操作，<code>each()</code> 方法是最佳选择。 </p> 			<p ibm-back-to-top=""><a href="http://www.ibm.com/developerworks/cn/java/j-pg04149.html#ibm-pcon"><br /></a></p><p><a name="N102DF">Range 迭代</a></p> 			<p> Groovy 提供了原生的 <code>Range</code> 类型，可以直接迭代。使用两个点分隔的所有内容（比如 <code>1..10</code>）都是一个 <code>Range</code>。清单 14 展示了这个例子： </p> 			<br /><a name="listing14"><strong>清单 14. Range 迭代</strong></a><br /><table border="0" cellpadding="0" cellspacing="0" height="92" width="361"><tbody><tr><td><pre>def range = 5..10 range.each{<br />&nbsp;println it<br />&nbsp;}<br />&nbsp;//output: 5 6 7 8 9 10 </pre></td></tr></tbody></table><br /> 			<p> 				<code>Range</code> 不局限于简单的 <code>Integer</code>。考虑清单 15 在的代码，其中迭代 <code>Date</code> 的 <code>Range</code>： </p> 			<br /><a name="listing15"><strong>清单 15. <code>Date</code> 迭代</strong></a><br /><table border="0" cellpadding="0" cellspacing="0" height="254" width="447"><tbody><tr><td><pre>def today = new Date()<br />&nbsp;def nextWeek = today + 7<br />&nbsp;(today..nextWeek).each{<br />&nbsp;println it<br />&nbsp;} <br /> //output: Thu Mar 12 04:49:35 MDT 2009<br />&nbsp;Fri Mar 13 04:49:35 MDT 2009 <br />Sat Mar 14 04:49:35 MDT 2009 <br />Sun Mar 15 04:49:35 MDT 2009 <br />Mon Mar 16 04:49:35 MDT 2009 <br />Tue Mar 17 04:49:35 MDT 2009 <br />Wed Mar 18 04:49:35 MDT 2009 <br />Thu Mar 19 04:49:35 MDT 2009 </pre></td></tr></tbody></table><br /> 			<p> 可以看到，<code>each()</code> 准确地出现在您所期望的位置。Java 语言缺乏原生的 <code>Range</code> 类型，但是提供了一个类似地概念，采取 <code>enum</code> 的形式。毫不奇怪，在这里 <code>each()</code> 仍然派得上用场。</p> 			<p ibm-back-to-top=""><a href="http://www.ibm.com/developerworks/cn/java/j-pg04149.html#ibm-pcon"><br /></a></p><p><a name="N10332">Enumeration 类型</a></p> 			<p> Java <code>enum</code> 是按照特定顺序保存的随意的值集合。清单 16 展示了 <code>each()</code> 方法如何自然地配合 <code>enum</code>，就好象它在处理 <code>Range</code> 操作符一样： </p> 			<br /><a name="listing16"><strong>清单 16. <code>enum</code> 迭代</strong></a><br /><table border="0" cellpadding="0" cellspacing="0" height="200" width="417"><tbody><tr><td><pre>enum DAY{<br />&nbsp;MONDAY, TUESDAY, WEDNESDAY, THURSDAY,<br />&nbsp;FRIDAY, SATURDAY, SUNDAY<br />&nbsp;}<br />&nbsp;DAY.each{<br />&nbsp;println it<br />&nbsp;}<br />&nbsp;(DAY.MONDAY..DAY.FRIDAY).each{<br />&nbsp;println it<br />&nbsp;} </pre></td></tr></tbody></table><br /> 			<p> 在 Groovy 中，有些情况下，<code>each()</code> 这个名称远未能表达它的强大功能。在下面的例子中，将看到使用特定于所用上下文的方法对 <code>each()</code> 方法进行修饰。Groovy <code>eachRow()</code> 方法就是一个很好的例子。 </p> 			<p ibm-back-to-top=""><a href="http://www.ibm.com/developerworks/cn/java/j-pg04149.html#ibm-pcon"><br /></a></p><p><a name="N10368">SQL 迭代</a></p> 			<p> 在处理关系数据库表时，经常会说 &#8220;我需要针对表中的每一行执行操作&#8221;。比较一下前面的例子。您很可能会说 &#8220;我需要对列表中的每一种语言执行一些操作&#8221;。根据这个道理，<code>groovy.sql.Sql</code> 对象提供了一个 <code>eachRow()</code> 方法，如清单 17 所示： </p> 			<br /><a name="listing17"><strong>清单 17. <code>ResultSet</code> 迭代</strong></a><br /><table border="0" cellpadding="0" cellspacing="0" height="308" width="496"><tbody><tr><td><pre>import groovy.sql.*<br />&nbsp;def sql = Sql.newInstance(<br />&nbsp;"jdbc:derby://localhost:1527/MyDbTest;create=true",<br />&nbsp;"username",<br />&nbsp;"password",<br />&nbsp;"org.apache.derby.jdbc.ClientDriver")<br />&nbsp;println("grab a specific field")<br />&nbsp;sql.eachRow("select name from languages"){ row -&gt;<br />&nbsp;println row.name<br />&nbsp;}<br />&nbsp;println("grab all fields")<br />&nbsp;sql.eachRow("select * from languages"){ row -&gt;<br />&nbsp;println("Name: ${row.name}")<br />&nbsp;println("Version: ${row.version}")<br />&nbsp;println("URL: ${row.url}\n")<br />&nbsp;} </pre></td></tr></tbody></table><br /> 			<p> 该脚本的第一行代码实例化了一个新的 <code>Sql</code> 对象：设置 JDBC 连接字符串、用户名、密码和 JDBC 驱动器类。这时，可以调用 <code>eachRow()</code> 方法，传递 SQL <code>select</code> 语句作为一个方法参数。在闭包内部，可以引用列名（<code>name</code>、<code>version</code>、<code>url</code>），就好像实际存在 <code>getName()</code>、<code>getVersion()</code> 和 <code>getUrl()</code> 方法一样。 </p> 			<p> 这显然要比 Java 语言中的等效方法更加清晰。在 Java 中，必须创建单独的 <code>DriverManager</code>、<code>Connection</code>、<code>Statement</code> 和 <code>JDBCResultSet</code>，然后必须在嵌套的 <code>try</code>/<code>catch</code>/<code>finally</code> 块中将它们全部清除。 </p> 			<p> 对于 <code>Sql</code> 对象，您会认为 <code>each()</code> 或 <code>eachRow()</code> 都是一个合理的方法名。但是在接下来的示例中，我想您会认为 <code>each()</code> 这个名称并不能充分表达它的功能。 </p> 			<p ibm-back-to-top=""><a href="http://www.ibm.com/developerworks/cn/java/j-pg04149.html#ibm-pcon"><br /></a></p><p><a name="N103E0">文件迭代</a></p> 			<p> 我从未想过使用原始的 Java 代码逐行遍历 <code>java.io.File</code>。当我完成了所有的嵌套的 <code>BufferedReader</code> 和 <code>FileReader</code> 后（更别提每个流程末尾的所有异常处理），我已经忘记最初的目的是什么。 </p> 			<p> 清单 18 展示了使用 Java 语言完成的整个过程： </p> 			<br /><a name="listing18"><strong>清单 18. Java 文件迭代</strong></a><br /><table border="0" cellpadding="0" cellspacing="0" height="578" width="487"><tbody><tr><td><pre>import java.io.BufferedReader;<br />&nbsp;import java.io.FileNotFoundException;<br />&nbsp;import java.io.FileReader; import java.io.IOException;<br />&nbsp;public class WalkFile {<br />&nbsp;public static void main(String[] args) {<br />&nbsp;BufferedReader br = null;<br />&nbsp;try {<br />&nbsp;br = new BufferedReader(new FileReader("languages.txt"));<br />&nbsp;String line = null;<br />&nbsp;while((line = br.readLine()) != null) {<br />&nbsp;System.out.println("I know " + line);<br />&nbsp;}<br />&nbsp;}<br />&nbsp;catch(FileNotFoundException e) {<br />&nbsp;e.printStackTrace();<br />&nbsp;}<br />&nbsp;catch(IOException e) {<br />&nbsp;e.printStackTrace();<br />&nbsp;}<br />&nbsp;finally {<br />&nbsp;if(br != null) {<br />&nbsp;try {<br />&nbsp;br.close();<br />&nbsp;}             <br />catch(IOException e) {<br />&nbsp;e.printStackTrace();<br />&nbsp;}<br />&nbsp;}<br />&nbsp;}<br />&nbsp;}<br />&nbsp;} </pre></td></tr></tbody></table><br /> 			<p> 清单 19 展示了 Groovy 中的等效过程： </p> 			<br /><a name="listing19"><strong>清单 19. Groovy 文件迭代</strong></a><br /><table border="0" cellpadding="0" cellspacing="0" height="74" width="536"><tbody><tr><td><pre>def f = new File("languages.txt") f.eachLine{language-&gt;<br />&nbsp;println "I know ${language}"<br />} </pre></td></tr></tbody></table><br /> 			<p> 这正是 Groovy 的简洁性真正擅长的方面。现在，我希望您了解为什么我将 Groovy 称为 &#8220;<a href="http://www.ibm.com/developerworks/cn/java/j-pg02179.html">Java 程序员的 DSL</a>&#8221;。 </p> 			<p> 注意，我在 Groovy 和 Java 语言中同时处理同一个 <code>java.io.File</code> 类。如果该文件不存在，那么 Groovy 代码将抛出和 Java 代码相同的 <code>FileNotFoundException</code> 异常。区别在于，Groovy 没有已检测的异常。在 <code>try</code>/<code>catch</code>/<code>finally</code> 块中封装 <code>eachLine()</code> 结构是我自己的爱好 &#8212; 而不是一项语言需求。对于一个简单的命令行脚本中，我欣赏 <a href="http://www.ibm.com/developerworks/cn/java/j-pg04149.html#listing19">清单 19</a> 中的代码的简洁性。如果我在运行应用服务的同时执行相同的迭代，我不能对这些异常坐视不管。我将在与 Java 版本相同的 <code>try/catch</code> 块中封装 <code>eachLine()</code> 块。 </p> 			<p> 				<code>File</code> 类对 <code>each()</code> 方法进行了一些修改。其中之一就是 <code>splitEachLine(String separator, Closure closure)</code>。这意味着您不仅可以逐行遍历文件，同时还可以将它分为不同的标记。清单 20 展示了一个例子： </p> 			<br /><a name="listing20"><strong>清单 20. 分解文件的每一行 </strong></a><br /><table border="0" cellpadding="0" cellspacing="0" height="182" width="757"><tbody><tr><td><pre>// languages.txt<br />&nbsp;// notice the space between the language and the version Java 1.5 Groovy 1.6 JavaScript 1.x<br />&nbsp;// splitTest.groovy <br />def f = new File("languages.txt")<br />&nbsp;f.splitEachLine(" "){words-&gt;<br />&nbsp;words.each{ println it<br />&nbsp;}<br />&nbsp;} <br /> // output Java 1.5 Groovy 1.6 JavaScript 1.x </pre></td></tr></tbody></table><br /> 			<p> 如果处理的是二进制文件，Groovy 还提供了一个 <code>eachByte()</code> 方法。 </p> 			<p> 当然，Java 语言中的 <code>File</code> 并不总是一个文件 &#8212; 有时是一个目录。Groovy 还提供了一些 <code>each()</code> 修改以处理子目录。 </p> 			<p ibm-back-to-top=""><a href="http://www.ibm.com/developerworks/cn/java/j-pg04149.html#ibm-pcon"><br /></a></p><p><a name="N1046C">目录迭代</a></p> 			<p> 使用 Groovy 代替 shell 脚本（或批处理脚本）非常容易，因为您能够方便地访问文件系统。要获得当前目录的目录列表，参见清单 21：</p> 			<br /><a name="listing21"><strong>清单 21. 目录迭代</strong></a><br /><table border="0" cellpadding="0" cellspacing="0" height="74" width="443"><tbody><tr><td><pre>def dir = new File(".") dir.eachFile{file-&gt;<br />&nbsp;println file<br />&nbsp;} </pre></td></tr></tbody></table><br /> 			<p> 				<code>eachFile()</code> 方法同时返回了文件和子目录。使用 Java 语言的 <code>isFile()</code> 和 <code>isDirectory()</code> 方法，可以完成更复杂的事情。清单 22 展示了一个例子： </p> 			<br /><a name="listing22"><strong>清单 22. 分离文件和目录</strong></a><br /><table border="0" cellpadding="0" cellspacing="0" height="182" width="427"><tbody><tr><td><pre>def dir = new File(".") dir.eachFile{file-&gt;<br />&nbsp;if(file.isFile()){<br />&nbsp;println "FILE: ${file}"<br />&nbsp;}else if(file.isDirectory()){<br />&nbsp;println "DIR:  ${file}"<br />&nbsp;}else{<br />&nbsp;println "Uh, I'm not sure what it is..."<br />&nbsp;}<br />&nbsp;} </pre></td></tr></tbody></table><br /> 			<p> 由于两种 Java 方法都返回 <code>boolean</code> 值，可以在代码中添加一个 Java 三元操作符。清单 23 展示了一个例子： </p> 			<br /><a name="listing23"><strong>清单 23. 三元操作符</strong></a><br /><table border="0" cellpadding="0" cellspacing="0" height="92" width="546"><tbody><tr><td><pre>def dir = new File(".")<br />&nbsp;dir.eachFile{file-&gt;<br />&nbsp;println file.isDirectory() ? "DIR:  ${file}" : "FILE: ${file}"<br />&nbsp;} </pre></td></tr></tbody></table><br /> 			<p> 如果只对目录有兴趣，那么可以使用 <code>eachDir()</code> 而不是 <code>eachFile()</code>。还提供了 <code>eachDirMatch()</code> 和 <code>eachDirRecurse()</code> 方法。 </p> 			<p> 可以看到，对 <code>File</code> 仅使用 <code>each()</code> 方法并不能提供足够的含义。典型 <code>each()</code> 方法的语义保存在 <code>File</code> 中，但是方法名更具有描述性，从而提供更多有关这个高级功能的信息。 </p> 			<p ibm-back-to-top=""><a href="http://www.ibm.com/developerworks/cn/java/j-pg04149.html#ibm-pcon"><br /></a></p><p><a name="N104CF">URL 迭代</a></p> 			<p> 理解了如何遍历 <code>File</code> 后，可以使用相同的原则遍历 HTTP 请求的响应。Groovy 为 <code>java.net.URL</code> 提供了一个方便的（和熟悉的）<code>eachLine()</code> 方法。 </p> 			<p> 例如，清单 24 将逐行遍历 ibm.com 主页的 HTML： </p> 			<br /><a name="listing24"><strong>清单 24. URL 迭代</strong></a><br /><table border="0" cellpadding="0" cellspacing="0" height="92" width="416"><tbody><tr><td><pre>def url = new URL("http://www.ibm.com")<br />&nbsp;url.eachLine{line-&gt;<br />&nbsp;println line<br />&nbsp;} </pre></td></tr></tbody></table><br /> 			<p> 当然，如果这就是您的目的的话，Groovy 提供了一个只包含一行代码的解决办法，这主要归功于 <code>toURL()</code> 方法，它被添加到所有 <code>Strings</code>：<code>"http://www.ibm.com".toURL().eachLine{ println it }</code>。 </p> 			<p> 但是，如果希望对 HTTP 响应执行一些更有用的操作，该怎么办呢？具体来讲，如果发出的请求指向一个 RESTful Web 服务，而该服务包含您要解析的 XML，该怎么做呢？<code>each()</code> 方法将在这种情况下提供帮助。 </p> 			<p ibm-back-to-top=""><a href="http://www.ibm.com/developerworks/cn/java/j-pg04149.html#ibm-pcon"><br /></a></p><p><a name="N10507">XML 迭代</a></p> 			<p> 您已经了解了如何对文件和 URL 使用 <code>eachLine()</code> 方法。XML 给出了一个稍微有些不同的问题 &#8212; 与逐行遍历 XML 文档相比，您可能更希望对逐个元素进行遍历。 </p> 			<p> 例如，假设您的语言列表存储在名为 languages.xml 的文件中，如清单 25 所示： </p> 			<br /><a name="listing25"><strong>清单 25. languages.xml 文件</strong></a><br /><table border="0" cellpadding="0" cellspacing="0" height="110" width="431"><tbody><tr><td><pre>&lt;langs&gt;<br />&nbsp;&lt;language&gt;Java&lt;/language&gt;<br />&nbsp;&lt;language&gt;Groovy&lt;/language&gt;<br />&nbsp;&lt;language&gt;JavaScript&lt;/language&gt;<br />&nbsp;&lt;/langs&gt; </pre></td></tr></tbody></table><br /> 			<p> Groovy 提供了一个 <code>each()</code> 方法，但是需要做一些修改。如果使用名为 <code>XmlSlurper</code> 的原生 Groovy 类解析 XML，那么可以使用 <code>each()</code> 遍历元素。参见清单 26 所示的例子： </p> 			<br /><a name="listing26"><strong>清单 26. XML 迭代</strong></a><br /><table border="0" cellpadding="0" cellspacing="0" height="110" width="448"><tbody><tr><td><pre>def langs = new XmlSlurper().parse("languages.xml")<br />&nbsp;langs.language.each{<br />&nbsp;println it<br />&nbsp;}<br />&nbsp;//output Java Groovy JavaScript </pre></td></tr></tbody></table><br /> 			<p> 				<code>langs.language.each</code> 语句从名为 <code>&lt;language&gt;</code> 的 <code>&lt;langs&gt;</code> 提取所有元素。如果同时拥有 <code>&lt;format&gt;</code> 和 <code>&lt;server&gt;</code> 元素，它们将不会出现在 <code>each()</code> 方法的输出中。</p> 			<p> 如果觉得这还不够的话，那么假设这个 XML 是通过一个 RESTful Web 服务的形式获得，而不是文件系统中的文件。使用一个 URL 替换文件的路径，其余代码仍然保持不变，如清单 27 所示： </p> 			<br /><a name="listing27"><strong>清单 27. Web 服务调用的 XML 迭代</strong></a><br /><table border="0" cellpadding="0" cellspacing="0" height="92" width="609"><tbody><tr><td><pre>def langs = new XmlSlurper().parse("http://somewhere.com/languages")<br />&nbsp;langs.language.each{<br />&nbsp;println it<br />&nbsp;} </pre></td></tr></tbody></table><br /> 			<p> 这真是个好方法，<code>each()</code> 方法在这里用得很好，不是吗？</p> 			<p ibm-back-to-top=""><a href="http://www.ibm.com/developerworks/cn/java/j-pg04149.html#ibm-pcon"><br /></a></p><p><a name="N1056B">结束语</a></p> 			<p> 在使用 <code>each()</code> 方法的整个过程中，最妙的部分在于它只需要很少的工作就可以处理大量 Groovy 内容。解了 <code>each()</code> 方法之后，Groovy 中的迭代就易如反掌了。正如 Raymond 所说，这正是关键所在。一旦了解了如何遍历 <code>List</code>，那么很快就会掌握如何遍历数组、<code>Map</code>、<code>String</code>、<code>Range</code>、<code>enum</code>、SQL <code>ResultSet</code>、<code>File</code>、目录和 <code>URL</code>，甚至是 XML 文档的元素。 </p> 			<p> 本文的最后一个示例简单提到使用 <code>XmlSlurper</code> 实现 XML 解析。在下一期文章中，我将继续讨论这个问题，并展示使用 Groovy 进行 XML 解析有多么简单！您将看到 <code>XmlParser</code> 和 <code>XmlSlurper</code> 的实际使用，并更好地了解 Groovy 为什么提供两个类似但又略有不同的类实现 XML 解析。到那时，希望您能发现 Groovy 的更多实际应用。 </p></div><img src ="http://www.blogjava.net/ljc-java/aggbug/355737.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/ljc-java/" target="_blank">小罗</a> 2011-08-04 11:50 <a href="http://www.blogjava.net/ljc-java/archive/2011/08/04/355737.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>