Groovy如何帮助JavaFX:别了,纯粹的Java代码?
JavaFX天气应用使用这个海量Java类创建了如上的GUI,用以连接到Yahoo的气象服务。Java类连接到的完整的RSS种子如下所示:
<ol class="dp-xml"> <li class="alt"><span><span class="tag"><?</span><span class="tag-name">xml</span><span> </span><span class="attribute">version</span><span>=</span><span class="attribute-value">"1.0"</span><span> </span><span class="attribute">encoding</span><span>=</span><span class="attribute-value">"UTF-8"</span><span> </span><span class="attribute">standalone</span><span>=</span><span class="attribute-value">"yes"</span><span> </span><span class="tag">?></span><span> </span></span></li> <li class=""> <span></span><span class="tag"><</span><span class="tag-name">rss</span><span> </span><span class="attribute">version</span><span>=</span><span class="attribute-value">"2.0"</span><span> </span><span class="attribute">xmlns:yweather</span><span>=</span><span class="attribute-value">"http://xml.weather.yahoo.com/ns/rss/1.0"</span><span> </span><span class="attribute">xmlns:geo</span><span>=</span><span class="attribute-value">"http://www.w3.org/2003/01/geo/wgs84_pos#"</span><span class="tag">></span><span> </span> </li> <li class="alt"><span> </span></li> <li class=""> <span> </span><span class="tag"><</span><span class="tag-name">channel</span><span class="tag">></span><span> </span> </li> <li class="alt"><span> </span></li> <li class=""> <span> </span><span class="tag"><</span><span class="tag-name">title</span><span class="tag">></span><span>Yahoo! Weather - Prague, EZ</span><span class="tag"></</span><span class="tag-name">title</span><span class="tag">></span><span> </span> </li> <li class="alt"> <span> </span><span class="tag"><</span><span class="tag-name">link</span><span class="tag">></span><span>http://us.rd.yahoo.com/dailynews/rss/weather/Prague__EZ/*http://weather.yahoo.com/forecast/EZXX0012_f.html</span><span class="tag"></</span><span class="tag-name">link</span><span class="tag">></span><span> </span> </li> <li class=""> <span> </span><span class="tag"><</span><span class="tag-name">description</span><span class="tag">></span><span>Yahoo! Weather for Prague, EZ</span><span class="tag"></</span><span class="tag-name">description</span><span class="tag">></span><span> </span> </li> <li class="alt"> <span> </span><span class="tag"><</span><span class="tag-name">language</span><span class="tag">></span><span>en-us</span><span class="tag"></</span><span class="tag-name">language</span><span class="tag">></span><span> </span> </li> <li class=""> <span> </span><span class="tag"><</span><span class="tag-name">lastBuildDate</span><span class="tag">></span><span>Fri, 05 Jun 2009 8:00 pm CEST</span><span class="tag"></</span><span class="tag-name">lastBuildDate</span><span class="tag">></span><span> </span> </li> <li class="alt"> <span> </span><span class="tag"><</span><span class="tag-name">ttl</span><span class="tag">></span><span>60</span><span class="tag"></</span><span class="tag-name">ttl</span><span class="tag">></span><span> </span> </li> <li class=""><span> </span></li> <li class="alt"> <span> </span><span class="tag"><</span><span class="tag-name">yweather:location</span><span> </span><span class="attribute">city</span><span>=</span><span class="attribute-value">"Prague"</span><span> </span><span class="attribute">region</span><span>=</span><span class="attribute-value">""</span><span> </span><span class="attribute">country</span><span>=</span><span class="attribute-value">"EZ"</span><span class="tag">/></span><span> </span> </li> <li class=""> <span> </span><span class="tag"><</span><span class="tag-name">yweather:units</span><span> </span><span class="attribute">temperature</span><span>=</span><span class="attribute-value">"F"</span><span> </span><span class="attribute">distance</span><span>=</span><span class="attribute-value">"mi"</span><span> </span><span class="attribute">pressure</span><span>=</span><span class="attribute-value">"in"</span><span> </span><span class="attribute">speed</span><span>=</span><span class="attribute-value">"mph"</span><span class="tag">/></span><span> </span> </li> <li class="alt"> <span> </span><span class="tag"><</span><span class="tag-name">yweather:wind</span><span> </span><span class="attribute">chill</span><span>=</span><span class="attribute-value">"54"</span><span> </span><span class="attribute">direction</span><span>=</span><span class="attribute-value">"60"</span><span> </span><span class="attribute">speed</span><span>=</span><span class="attribute-value">"3"</span><span> </span><span class="tag">/></span><span> </span> </li> <li class=""> <span> </span><span class="tag"><</span><span class="tag-name">yweather:atmosphere</span><span> </span><span class="attribute">humidity</span><span>=</span><span class="attribute-value">"58"</span><span> </span><span class="attribute">visibility</span><span>=</span><span class="attribute-value">"6.21"</span><span> </span><span class="attribute">pressure</span><span>=</span><span class="attribute-value">"29.8"</span><span> </span><span class="attribute">rising</span><span>=</span><span class="attribute-value">"0"</span><span> </span><span class="tag">/></span><span> </span> </li> <li class="alt"> <span> </span><span class="tag"><</span><span class="tag-name">yweather:astronomy</span><span> </span><span class="attribute">sunrise</span><span>=</span><span class="attribute-value">"4:56 am"</span><span> </span><span class="attribute">sunset</span><span>=</span><span class="attribute-value">"9:06 pm"</span><span class="tag">/></span><span> </span> </li> <li class=""><span> </span></li> <li class="alt"> <span> </span><span class="tag"><</span><span class="tag-name">image</span><span class="tag">></span><span> </span> </li> <li class=""> <span> </span><span class="tag"><</span><span class="tag-name">title</span><span class="tag">></span><span>Yahoo! Weather</span><span class="tag"></</span><span class="tag-name">title</span><span class="tag">></span><span> </span> </li> <li class="alt"> <span> </span><span class="tag"><</span><span class="tag-name">width</span><span class="tag">></span><span>142</span><span class="tag"></</span><span class="tag-name">width</span><span class="tag">></span><span> </span> </li> <li class=""> <span> </span><span class="tag"><</span><span class="tag-name">height</span><span class="tag">></span><span>18</span><span class="tag"></</span><span class="tag-name">height</span><span class="tag">></span><span> </span> </li> <li class="alt"> <span> </span><span class="tag"><</span><span class="tag-name">link</span><span class="tag">></span><span>http://weather.yahoo.com</span><span class="tag"></</span><span class="tag-name">link</span><span class="tag">></span><span> </span> </li> <li class=""> <span> </span><span class="tag"><</span><span class="tag-name">url</span><span class="tag">></span><span>http://l.yimg.com/a/i/us/nws/th/main_142b.gif</span><span class="tag"></</span><span class="tag-name">url</span><span class="tag">></span><span> </span> </li> <li class="alt"> <span> </span><span class="tag"></</span><span class="tag-name">image</span><span class="tag">></span><span> </span> </li> <li class=""><span> </span></li> <li class="alt"> <span> </span><span class="tag"><</span><span class="tag-name">item</span><span class="tag">></span><span> </span> </li> <li class=""><span> </span></li> <li class="alt"> <span> </span><span class="tag"><</span><span class="tag-name">title</span><span class="tag">></span><span>Conditions for Prague, EZ at 8:00 pm CEST</span><span class="tag"></</span><span class="tag-name">title</span><span class="tag">></span><span> </span> </li> <li class=""> <span> </span><span class="tag"><</span><span class="tag-name">geo:lat</span><span class="tag">></span><span>50.1</span><span class="tag"></</span><span class="tag-name">geo:lat</span><span class="tag">></span><span> </span> </li> <li class="alt"> <span> </span><span class="tag"><</span><span class="tag-name">geo:long</span><span class="tag">></span><span>14.28</span><span class="tag"></</span><span class="tag-name">geo:long</span><span class="tag">></span><span> </span> </li> <li class=""> <span> </span><span class="tag"><</span><span class="tag-name">link</span><span class="tag">></span><span>http://us.rd.yahoo.com/dailynews/rss/weather/Prague__EZ/*http://weather.yahoo.com/forecast/EZXX0012_f.html</span><span class="tag"></</span><span class="tag-name">link</span><span class="tag">></span><span> </span> </li> <li class="alt"> <span> </span><span class="tag"><</span><span class="tag-name">pubDate</span><span class="tag">></span><span>Fri, 05 Jun 2009 8:00 pm CEST</span><span class="tag"></</span><span class="tag-name">pubDate</span><span class="tag">></span><span> </span> </li> <li class=""> <span> </span><span class="tag"><</span><span class="tag-name">yweather:condition</span><span> </span><span class="attribute">text</span><span>=</span><span class="attribute-value">"Partly Cloudy"</span><span> </span><span class="attribute">code</span><span>=</span><span class="attribute-value">"30"</span><span> </span><span class="attribute">temp</span><span>=</span><span class="attribute-value">"54"</span><span> </span><span class="attribute">date</span><span>=</span><span class="attribute-value">"Fri, 05 Jun 2009 8:00 pm CEST"</span><span> </span><span class="tag">/></span><span> </span> </li> <li class="alt"> <span> </span><span class="tag"><</span><span class="tag-name">description</span><span class="tag">></span><span class="cdata"><![CDATA[ </span> </li> <li class=""><span><span class="cdata"><img src="http://l.yimg.com/a/i/us/we/52/30.gif"/> </span> </span></li> <li class="alt"><span><span class="cdata"><b>Current Conditions:</b> </span> </span></li> <li class=""><span><span class="cdata">Partly Cloudy, 54 F </span> </span></li> <li class="alt"><span><span class="cdata"><b>Forecast:</b> </span> </span></li> <li class=""><span><span class="cdata">Fri - Partly Cloudy. High: 58 Low: 42 </span> </span></li> <li class="alt"><span><span class="cdata">Sat - PM Rain. High: 58 Low: 49 </span> </span></li> <li class=""><span><span class="cdata"><a href="http://us.rd.yahoo.com/dailynews/rss/weather/Prague__EZ/*http://weather.yahoo.com/forecast/EZXX0012_f.html">Full Forecast at Yahoo! Weather</a>(provided by The Weather Channel) </span> </span></li> <li class="alt"><span><span class="cdata"></span> </span></li> <li class=""><span><span class="cdata">]]></span><span> </span></span></li> <li class="alt"> <span> </span><span class="tag"></</span><span class="tag-name">description</span><span class="tag">></span><span> </span> </li> <li class=""> <span> </span><span class="tag"><</span><span class="tag-name">yweather:forecast</span><span> </span><span class="attribute">day</span><span>=</span><span class="attribute-value">"Fri"</span><span> </span><span class="attribute">date</span><span>=</span><span class="attribute-value">"5 Jun 2009"</span><span> </span><span class="attribute">low</span><span>=</span><span class="attribute-value">"42"</span><span> </span><span class="attribute">high</span><span>=</span><span class="attribute-value">"58"</span><span> </span><span class="attribute">text</span><span>=</span><span class="attribute-value">"Partly Cloudy"</span><span> </span><span class="attribute">code</span><span>=</span><span class="attribute-value">"29"</span><span> </span><span class="tag">/></span><span> </span> </li> <li class="alt"> <span> </span><span class="tag"><</span><span class="tag-name">yweather:forecast</span><span> </span><span class="attribute">day</span><span>=</span><span class="attribute-value">"Sat"</span><span> </span><span class="attribute">date</span><span>=</span><span class="attribute-value">"6 Jun 2009"</span><span> </span><span class="attribute">low</span><span>=</span><span class="attribute-value">"49"</span><span> </span><span class="attribute">high</span><span>=</span><span class="attribute-value">"58"</span><span> </span><span class="attribute">text</span><span>=</span><span class="attribute-value">"PM Rain"</span><span> </span><span class="attribute">code</span><span>=</span><span class="attribute-value">"12"</span><span> </span><span class="tag">/></span><span> </span> </li> <li class=""> <span> </span><span class="tag"><</span><span class="tag-name">guid</span><span> </span><span class="attribute">isPermaLink</span><span>=</span><span class="attribute-value">"false"</span><span class="tag">></span><span>EZXX0012_2009_06_05_20_00_CEST</span><span class="tag"></</span><span class="tag-name">guid</span><span class="tag">></span><span> </span> </li> <li class="alt"><span> </span></li> <li class=""> <span> </span><span class="tag"></</span><span class="tag-name">item</span><span class="tag">></span><span> </span> </li> <li class="alt"><span> </span></li> <li class=""> <span> </span><span class="tag"></</span><span class="tag-name">channel</span><span class="tag">></span><span> </span> </li> <li class="alt"><span> </span></li> <li class=""> <span></span><span class="tag"></</span><span class="tag-name">rss</span><span class="tag">></span><span> </span> </li> </ol>
现在,不管你有没有听说过“Groovy”,你都要考虑一下“老土的工作”(译注:grunt work,Groovy的谐音)。这正是Groovy尤其擅长的地方。一个相关的有力案例是web服务。还有,其HTML和XML的解析。因此,一旦你需要在Java应用中与web服务交互,最应该考虑的助手显然就是Groovy了。
看一下上面的RSS然后看看下面Groovy代码的11行,以下是相关代码:
<ol class="dp-j"><li class="alt"><span><span>def channel = </span><span class="keyword"><strong><font color="#006699">new</font></strong></span><span> XmlParser().parse(url).channel </span></span></li></ol>
这一行给了你上面的RSS种子的“channel”元素!了不起,是不是?而从那儿开始,下面的Groovy脚本对RSS种子进行解析,确切地识别出那些JavaFX GUI感兴趣的部分,然后用了大约20行,产生出的结果,跟通常约需250行才能做出来的一模一样。
看看下面的Groovy代码段,跟上面的RSS种子对比一下,来了解它是如何工作的。注意,这甚至还不是代码段!这就是Groovy web service类的全部了。这简直就是酷毙了,尤其是再把它跟原来的丑陋代码比对过之后。
<ol class="dp-j"> <li class="alt"><span><span class="keyword"><strong><font color="#006699">package</font></strong></span><span> weatherfx.service </span></span></li> <li class=""><span> </span></li> <li class="alt"> <span></span><span class="keyword"><strong><font color="#006699">class</font></strong></span><span> YahooWeatherServiceG { </span> </li> <li class=""> <span> </span><span class="keyword"><strong><font color="#006699">static</font></strong></span><span> YW = </span><span class="keyword"><strong><font color="#006699">new</font></strong></span><span> groovy.xml.Namespace(</span><span class="string"><font color="#0000ff">"http://xml.weather.yahoo.com/ns/rss/1.0"</font></span><span>) </span> </li> <li class="alt"><span> </span></li> <li class=""><span> def forecasts </span></li> <li class="alt"><span> </span></li> <li class=""> <span> YahooWeatherServiceG(String code, </span><span class="keyword"><strong><font color="#006699">boolean</font></strong></span><span> celsius) { </span> </li> <li class="alt"> <span> def url = </span><span class="string"><font color="#0000ff">"http://weather.yahooapis.com/forecastrss?u=f&p=$code"</font></span><span> </span> </li> <li class=""><span> println url.toURL().text </span></li> <li class="alt"> <span> def channel = </span><span class="keyword"><strong><font color="#006699">new</font></strong></span><span> XmlParser().parse(url).channel </span> </li> <li class=""> <span> cityName = channel[YW.location].</span><span class="annotation"><font color="#646464">@city</font></span><span> </span> </li> <li class="alt"><span> def wind = channel[YW.wind].first() </span></li> <li class=""> <span> windSpeed = wind.</span><span class="annotation"><font color="#646464">@speed</font></span><span>.toInteger() </span> </li> <li class="alt"> <span> windDirection = wind.</span><span class="annotation"><font color="#646464">@direction</font></span><span>.toInteger() </span> </li> <li class=""><span> def cond = channel.item[YW.condition].first() </span></li> <li class="alt"> <span> temp = cond.</span><span class="annotation"><font color="#646464">@temp</font></span><span>.toInteger() </span> </li> <li class=""><span> forecasts = channel.item[YW.forecast] </span></li> <li class="alt"><span> } </span></li> <li class=""><span> </span></li> <li class="alt"><span> String cityName </span></li> <li class=""> <span> </span><span class="keyword"><strong><font color="#006699">int</font></strong></span><span> temp </span> </li> <li class="alt"> <span> </span><span class="keyword"><strong><font color="#006699">int</font></strong></span><span> windSpeed </span> </li> <li class=""> <span> </span><span class="keyword"><strong><font color="#006699">int</font></strong></span><span> windDirection </span> </li> <li class="alt"><span> </span></li> <li class=""> <span> </span><span class="keyword"><strong><font color="#006699">int</font></strong></span><span> getConditionCode(</span><span class="keyword"><strong><font color="#006699">int</font></strong></span><span> day=</span><span class="number"><font color="#c00000">0</font></span><span>) { forecasts[day].</span><span class="annotation"><font color="#646464">@code</font></span><span>.toInteger() } </span> </li> <li class="alt"> <span> </span><span class="keyword"><strong><font color="#006699">int</font></strong></span><span> getLowsTemp (</span><span class="keyword"><strong><font color="#006699">int</font></strong></span><span> day=</span><span class="number"><font color="#c00000">0</font></span><span>) { forecasts[day].</span><span class="annotation"><font color="#646464">@low</font></span><span>.toInteger() } </span> </li> <li class=""> <span> </span><span class="keyword"><strong><font color="#006699">int</font></strong></span><span> getHighsTemp (</span><span class="keyword"><strong><font color="#006699">int</font></strong></span><span> day=</span><span class="number"><font color="#c00000">0</font></span><span>) { forecasts[day].</span><span class="annotation"><font color="#646464">@high</font></span><span>.toInteger() } </span> </li> <li class="alt"><span>} </span></li> </ol>
现在,停下来想象一下,上述代码跟其原始纯粹的Java代码相比起来,(a)测试和(b)维护是不是要简单、高效得多,且不易出错?
不过,目前还没有一个JavaFX/Groovy的交叉编译器。因此,怎样去把你的Java web service代码替换为上面的Groovy代码呢?首先创建一个独立的工程来建立你的Groovy web service。然后,将该工程添加进你的JavaFX应用的classpath。接着,将你的JavaFX应用中的两三个指向原始Java类的引用替换为指向Groovy class (经编译后现在已是Java类) 。
上述这一段做完之后如下图所示:
然后运行JavaFX应用,你将得到跟先前一样的结果,不同的是web service代码现在是由Groovy处理了。还有,就是你的应用中再也没有纯粹的Java代码了。哦,亲爱的。JavaFX创建了GUI,而Groovy则在后台干些脏累活(grunt work)。那么,该对纯粹的Java代码说声再见了?
编者注:首先应该说明一下,虽然JavaFX与Java之间可互操作,JavaFX在语法上也从Java中继承了很多,但是脚本语言JavaFX和Java并不是同一个概念。不过这篇文章自发布在JavaLobby上之后,得到了很多国外程序员的关注。当然,其中有人指出了作者犯的这个概念性错误:
“JavaFX不是Java!这批文章应该发表在Groovy区,它不应该出现在Java区,因为它跟Java毫不相关。”
不过,更多的讨论是围绕着Java平台语言未来的走向,以及做为程序员的选择而进行的。以下,51CTO编辑节选了部分精彩的讨论内容:
我喜欢groovy,但我们还是需要Java的。Groovy的性能以及内存消耗跟Java相比起来是可笑的。相比使用Groovy来取代Java(原文是replace better Java,即取代更好的Java),我更愿意选择Scala:它支持带静态类型的函数式编程,同时得益于类型推导,拥有许多优点。
――――――
我认为性能不是问题,在动态语言方面VM(虚拟机)正变得越来越“聪明”。我有时使用groovy,“真的”很喜欢它,不过就是不习惯用动态类型。用它我省下的时间都被BUG吹飞了,除非编译器能捕获到它。这就是为什么我的第二选项是Scala,只要它有了更多的工具和供应商支持。
――――――
对于大部分应用而言,Groovy表现已足够好(就像Ruby和PHP一样),Groovy代码不够快的时候,你可以写Java代码。此二者的集成是无缝的。同样,如果你不喜欢动态类型,那就使用静态类型吧。没人阻止你在Groovy里用静态类型。这正是它很棒的地方之一。如果你担心bug的困扰,因为自己使用了动态语言,我建议你进行单元测试,并使用一个好的持久的集成服务器。
――――――
Groovy是“作为平台的Java”之上的一个更高级的语言,JavaFX甚至还要高些。Groovy、JavaFX和Java作为语言是“作为平台的Java”的不同的味道。在JavaLobby里面出现Groovy和JavaFX的新闻是有意义的。一旦某一天Groovy和JavaFX取得了压倒性的胜利,就有必要建立一个javafx区和Groovy区来避免每天出现过多的相关新闻,而java.dzone.com就将成为一个专门有关Java“语言”的社区。
之所以会这么说,是因为JavaFX是Sun(Oracle)提供的新基石,这是需要的,也不错。JavaFX可与Flash/Flex/AIR竞争,Groovy则有自己的位置,但是许多人(包括我在内)认为脚本语言管理复杂应用是有问题的,因为缺少编译器(是的我知道你可以编译Groovy)。给“非纯粹Java代码”唱颂歌可以理解,尽管这会促使就使用正确技术作出坏的决定。
脚本有用武之地,真的,在小规模应用(或者重复同样模式的应用)以及需要从外部读取源码的高度动态的应用。
我已经一遍又一遍地读过这样的例子:
def channel = new XmlParser().parse(url).channel