groovy 2.5.4
Groovy是JVM的类似于Java的动态语言,随着时间的流逝,它就像好酒一样成熟。 在2007年1月成功发布Groovy 1.0之后,带有1.5标签的下一个主要里程碑已经上架。 随之而来的是我们将在本文中研究的几个有趣的新颖性。 该语言的主要补充是对Java 5功能的注释,泛型和枚举的支持,使Groovy 成为JVM的唯一替代动态语言,完全支持Spring,Hibernate,JPA ,Google Guice或TestNG 等框架 。 除了Java 5的新功能之外,该语言还提供了一些语法增强功能,以及更强大的动态行为自定义,类固醇的Swing UI构建器和改进的工具支持。
Groovier Groovy及其重要性
Groovy的主要卖点一直是与Java的无缝集成 。 您可以通过非常简单的方式将Groovy和Java类混合和匹配在一起:您可能有一个Groovy类,扩展了实现Groovy接口的Java类,反之亦然。 不幸的是,大多数其他替代JVM语言都不允许您无缝地交换两种不同语言的类。 因此,如果您想在工作中使用最好的语言而又不影响良好的类层次结构,那么您将没有太多选择,并且Groovy为您提供了以最透明的方式集成这两种语言的所有自由。
Groovy与Java共享相同的库,相同的对象模型,相同的线程模型和相同的安全模型。 在某种程度上,您可以将Groovy视为Java项目的实现细节, 而不必承担有问题的阻抗不匹配问题 。
Groovy是Java,而Groovy使Java变得更时髦。 与其他语言相比,由于非常相似的语法,Groovy当然是向Java开发人员提供最平坦的学习曲线的语言。
要记住,这尤其重要,因为Groovy会生成普通的Java字节码并使用常规的JDK库,因此您无需学习全新的API或具有复杂的集成机制:Groovy和Java即开即用。 额外的好处是,您可以保护 开发人员 , 昂贵的应用程序服务器 ,第三方库或本地库在Java 技能方面 的投资 ,因为您可以重用所有这些库 ,而不会遇到Groovy的问题。
说到调用JDK,第三方或内部库,不支持强类型化的替代语言不能总是调用所有Java方法,因为它们不能选择同一方法的特定多态变体。 。 在选择其他语言来提高工作效率或使代码更具可读性时,如果需要调用其他Java类,则在选择语言时必须非常小心,因为在开发过程中可能会遇到一些障碍。方式。
如今,所有主要的企业框架都要求使用注释,枚举或泛型之类的功能,以便最大程度地利用它们。 幸运的是,使用Groovy 1.5,开发人员可以从项目中所有这些Java 5功能的支持中受益。 让我们看看如何从Groovy中使用注释,枚举和泛型。
Java 5新增功能
Groovy编译器始终会生成与旧版Java VM兼容的Java字节码,但所使用的核心库依赖于JDK 1.4。 但是,对于其中的某些Java 5附加内容,需要使用Java 5字节码,因此,例如,生成的类可能包含表示具有运行时保留策略的注释的字节码信息。 因此,尽管Groovy 1.5可以在JDK 1.4上运行,但是其中某些功能只能在JDK 5上使用-在这种情况下,本文将对此进行介绍。
可变参数
在Java 5中,创建了省略号来表示具有可变长度参数的方法。 有了那些小的三点,Java允许用户在方法的末尾放置相同类型的尽可能多的参数-实际上,vararg参数只是该类型的元素的数组。 Groargy 1.0中已经存在Varargs-并且它们仍可与JDK 1.4运行时一起使用,但是很好地展示了如何使用它们。 基本上,只要方法的最后一个参数是对象数组或带三点的参数声明,就可以将多个参数传递给此方法。
第一个示例将使用省略号显示Groovy中varargs的用法:
int sum(int... someInts) {
def total = 0
for (int i = 0; i < someInts.size(); i++)
total += someInts[i]
return total
}
assert sum(1) == 1
assert sum(1, 2) == 3
assert sum(1, 2, 3) == 6
本示例中使用的断言说明了如何传递所需的整数。 有趣的是,为了与Java更好地语法兼容,Groovy中已添加了经典的for循环-尽管具有in关键字的groovier版本也可以透明地迭代各种数组或集合类型。
请注意,通过声明如下方法,即使将数组作为最后一个参数,也可能具有varargs支持:
int sum(int[] someInts) { /* */ }
该代码片段确实是微不足道的,并且显然有更多表达方式可用于计算总和。 例如,如果您有一个数字列表,则可以在一行代码中将所有数字相加:
assert [1, 2, 3].sum() == 6
Groovy中的Varargs不需要JDK 5作为底层Java运行时,这与我们现在将在下一节中介绍的注释不同。
注解
如支持Groovy编写其实体,控制器和组件的JBoss Seam的文档所示,可以使用@ Entity ,@ Id,@ Override等注释来装饰您的bean:
@Entity
@Name("hotel")
class Hotel implements Serializable
{
@Id @GeneratedValue
Long id
@Length(max=50) @NotNull
String name
@Length(max=100) @NotNull
String address
@Length(max=40) @NotNull
String city
@Length(min=2, max=10) @NotNull
String state
@Length(min=4, max=6) @NotNull
String zip
@Length(min=2, max=40) @NotNull
String country
@Column(precision=6, scale=2)
BigDecimal price
@Override
String toString() {
return "Hotel(${name}, ${address}, ${city}, ${zip})"
}
}
Hotel实体标有@Entity批注,并通过@Name为其命名。 可以像@Length注释约束中那样,将不同的参数传递给您的注释,其中可以设置不同的上限和下限以进行验证。 您还可以注意到Groovy属性在起作用:所有的getter和setter都在哪里? 公共或私有修饰符在哪里? 您不必等待Java 7或8获得属性! 按照约定,定义属性就像String country一样简单:私有国家字段将自动生成,公共获取器和设置器也将自动生成。 您的代码自然变得更加简洁易读 。
可以在类,字段,方法和方法参数上使用注释,例如在Java中。 但是,有两个陷阱需要注意。 首先,您可以在Groovy中使用注释,但是您尚不能定义它们-但是,在即将发布的Groovy版本中将可以使用。 其次,尽管语法与Java几乎100%相同,但是在注释的参数中传递值数组时还是有一点区别:Groovy要求使用方括号来代替大括号来包围元素,提供更统一的语法-因为Groovy列表和数组也使用方括号将其元素括起来。
使用Groovy 1.5中的注释,您可以轻松定义JPA或Hibernate注释的bean( http://www.curious-creature.org/2007/03/25/persistence-made-easy-with-groovy-and-jpa/ )在Groovy中,在您的Spring服务上添加一个@Transactional批注,使用TestNG和Fest( http://www.jroller.com/aalmiray/entry/testing_groovy_uis_with_fest )测试您的Swing UI。 可以在您的Groovy支持的项目中使用所有利用注释的有用且强大的企业框架。
枚举
每当您需要一组固定的相同类型的常量时,枚举就派上用场。 假设您需要一种干净的方法来定义几天的常量,而无需使用整数常量? 那么Enums是你的朋友。 以下代码段显示了如何定义星期几:
enum Day {
SUNDAY, MONDAY, TUESDAY, WEDNESDAY,
THURSDAY, FRIDAY, SATURDAY
}
定义枚举后,就可以像在Java中那样使用常规表示法Day.MONDAY来使用它,也可以用它来修饰 switch / case语句:
def today = Day.SATURDAY
switch (today) {
// Saturday or Sunday
case [Day.SATURDAY, Day.SUNDAY]:
println "Weekends are cool"
break
// a day between Monday and Friday
case Day.MONDAY..Day.FRIDAY:
println "Boring work day"
break
default :
println "Are you sure this is a valid day?"
}
请注意,Groovy的开关比类似C的语言的开关功能强大一点,因为可以在开关和外壳中使用任何类型的对象。 您不必将每个枚举的值堆叠到七个不同的case块中,而是可以将它们重新组合在列表或范围中:只要该值在列表或范围中,该大小写就为true并执行其关联的指令。
受Java教程启发的一个更复杂的示例从一个更天文学的角度来看枚举,并显示了枚举如何具有属性,构造函数和方法:
enum Planet {
MERCURY (3.303e+23, 2.4397e6),
VENUS (4.869e+24, 6.0518e6),
EARTH (5.976e+24, 6.37814e6),
MARS (6.421e+23, 3.3972e6),
JUPITER (1.9e+27, 7.1492e7),
SATURN (5.688e+26, 6.0268e7),
URANUS (8.686e+25, 2.5559e7),
NEPTUNE (1.024e+26, 2.4746e7)
double mass
double radius
Planet( double mass, double radius) {
this .mass = mass;
this .radius = radius;
}
void printMe() {
println "${name()} has a mass of ${mass} " +
"and a radius of ${radius}"
}
}
Planet.EARTH.printMe()
像注释一样,Groovy中的枚举要求运行JDK 5+,因为生成了Java 5字节码。
静态导入
在我们之前的枚举示例中,我们总是必须在枚举值之前为其父枚举类添加前缀,但是由于静态导入(即使在JDK 1.4运行时中也可以使用),我们可以通过删除Planet前缀来保存一些字符:
import static Planet.*
SATURN.printMe()
没有更多的行星前缀。 但是,当然,静态导入不仅适用于枚举,而且还适用于其他类和静态字段。 那做一些数学呢?
import static java.lang.Math.*
assert sin(PI / 6) + cos(PI / 3) == 1
静态导入了java.lang.Math的静态方法及其静态常量,以使表达式更加简洁。 但是,如果您无法理解正弦和余弦的缩写,则可以在Groovy中使用as关键字使用别名:
import static java.lang.Math.PI
import static java.lang.Math.sin as sine
import static java.lang.Math.cos as cosine
assert sine(PI / 6) + cosine(PI / 3) == 1
别名也可以与普通导入一起使用,而不仅仅是静态导入,并且可以为很多框架中发现的非常长的类名称添加一些快捷方式表示法,或者使用非显而易见的名称重命名方法或常量,或者不遵循它们而非常方便。您的命名约定标准。
泛型
Java 5更具争议性的功能也出现在带有Generics的最新版本Groovy 1.5中。 最初,毕竟将更多的键入信息添加到动态语言中可能感觉很奇怪。 Java开发人员通常认为,由于类型擦除(出于与旧版本Java的向后兼容的原因),类字节码中没有留下任何信息来表示通用类型。 但是,这是错误的,因为通过反射API,您可以对一个类进行自省,以发现其字段或带有泛型详细信息的方法参数的类型。
因此,例如,当您声明类型为List <String>的字段(在字节码中的某个位置)时,此信息将以某种元信息的形式保存,尽管该字段实际上只是类型为List 。 诸如JPA或Hibernate之类的企业框架使用这种自反信息,从而能够将实体中的元素集合与代表这些元素类型的实体相关联。
为了付诸实践,让我们检查泛型信息是否保留在类字段中:
class Talk {
String title
}
class Speaker {
String name
List<Talk> talks = []
}
def me = new Speaker(
name: 'Guillaume Laforge',
talks: [
new Talk(title: 'Groovy'),
new Talk(title: 'Grails')
])
def talksField = me.class.getDeclaredField('talks')
assert talksField.genericType.toString() ==
'java.util.Listt<Talk>'
我们定义了两个类:演讲者类,在会议上进行演讲。 在Speaker类中,talks属性的类型为List <Talk> 。 然后,我们用两个漂亮的快捷方式创建一个Speaker实例,以初始化name和talks属性,并创建一个Talk实例列表。 准备好此设置代码后,我们将检索代表对话的字段,并检查泛型类型信息是否正确:是的, talks是一个List ,但是一个Lists of Talks 。
协变返回类型
在Java 5中,如果子类中的方法具有与父类相同的名称和参数类型,但是具有从父方法的返回类型派生的返回类型,则可以覆盖父方法。 在Groovy 1.0中,不支持协变返回类型。 但是在Groovy 1.5中,您可以使用它们。 此外,如果您尝试使用返回类型不是父类方法的返回类型派生的方法来覆盖该方法,则将引发编译错误。 协变返回类型还可以与参数化类型一起使用。
除了对Java 5功能的支持(该语言增加了一些功能)之外,Groovy 1.5还引入了其他一些语法增强功能,我们将在以下部分中发现它们。
语法添加
猫王算子
除了Java 5的功能将注释,泛型和枚举引入Groovy之外,还有一个新的运算符进入了该语言:?:Elvis运算符。 当您看到有问题的操作员时,您将很容易猜出为什么以这种方式被昵称为-如果不是这样,请以Smiley的方式思考。 实际上,此新运算符是三元运算符的快捷方式。 如果变量的内容为null,则使用三进制运算符更改变量的值多少次才能为其分配一些默认值? Java中的典型情况如下:
String name ="Guillaume" ;
String displayName = name != null ? name : "Unknown";
在Groovy中,由于该语言能够根据需要将类型“强制”为布尔值(例如,需要条件表达式(如if或while构造中的条件表达式)),因此在此语句中,我们可以省略对null的比较,因为当String为null,将其强制为false,因此在Groovy中,该语句将变为:
String name ="Guillaume"
String displayName = name ? name : "Unknown"
但是,您仍然会注意到name变量的重复,这违反了DRY原则(请勿重复自己)。 由于这种构造非常普遍,因此引入了Elvis运算符以简化此类重复出现的情况,并且语句变为:
String name ="Guillaume"
String displayName = name ?: "Unknown"
名称变量的第二次出现被简单地省略,三元运算符不再是三元的,并且被缩短为这种更简洁的形式。
还值得注意的是,此新构造没有副作用,因为第一个元素(此处为名称)没有像三元运算符那样被两次求值,因此不需要引入一些中间临时值保留三元运算符的第一个元素的第一个求值结果。
经典循环
尽管Groovy严格来说并不是Java的100%超集,但是Groovy语法在每个发行版之后都更接近Java语法,并且越来越多的Java代码也是有效的Groovy。 这样做的最终好处是,当您开始使用Groovy时,可以将Java代码复制并粘贴到Groovy类中,这应该可以按预期工作。 然后,随着时间的流逝,当您学习该语言时,您开始使用GString(内插字符串)或闭包等方式丢弃Groovy中非强制性的分号。Groovy为Java开发人员提供了相当平坦的学习曲线。
但是,这种Java语法兼容性存在一个遗漏,因为Groovy不允许从Java C背景继承的经典for循环。 最初,Groovy开发人员认为这并不是所有语法中最好的语法,因此首选使用可读性更高的for / in构造。 但是,由于Groovy用户经常要求将此旧结构也包含在Groovy中,因此团队决定将其重新带回Groovy。
使用Groovy 1.5,您可以为/ in选择Groovy,也可以选择经典的for循环:
for (i in 0..9)
println i
for ( int i = 0; i < 10; i++)
println i
归根结底,这可能更多是个问题,并且长期使用Groovy的用户通常更喜欢使用最简洁的语法for / in循环。
命名参数不带括号
凭借其可扩展且简洁的语法以及先进的动态功能, Groovy是实现内部特定于域的语言的理想选择 。 如果您想在主题专家和开发人员之间共享一个通用的隐喻,则可以利用Groovy创建专用的业务语言,该语言可以为应用程序的关键概念和业务规则建模。 这些DSL的一个重要方面是使代码可读性强,并且非技术人员也更容易编写。 为了进一步实现此目标,对语言的语法进行了调整,以允许我们使用命名参数而无需使用括号。
首先,在Groovy中,命名参数如下所示:
fund.compare(to: benchmarkFund, in: euros)
compare(fund: someFund, to: benchmark, in: euros)
通过为数字添加新属性(这在Groovy中是可能的,但超出了本文的范围),我们还可以编写如下代码:
monster.move(left: 3.meters, at: 5.mph)
现在,通过省略括号,代码可以变得更加清晰,如下所示:
fund.compare to: benchmarkFund, in: euros
compare fund: someFund, to: benchmark, in: euros
monster.move left: 3.meters, at: 5.mph
显然,这没有什么大的区别,但是每个语句变得更接近真实的纯英语句子,并删除了宿主语言的常用样板技术代码。 Groovy语言语法的这种一点增强为业务DSL设计人员提供了更多选择。
改进的工具支持
Groovy还是一门年轻的语言时,一个常见的阻碍就是缺乏良好的工具支持:工具链和IDE支持都无法胜任。 幸运的是,随着Groovy和Grails Web框架的成熟和成功,这种情况已经改变。
引入“联合”编译器
Groovy以与Java的透明和无缝集成而闻名。 但这不仅仅在于能够从Groovy脚本中调用Java方法,不,两种语言之间的集成远不止于此。 例如,完全有可能有一个Groovy类扩展一个Java类,而该Java类又实现了Groovy接口,反之亦然。 不幸的是,这是其他替代语言始终不支持的东西。 但是,到目前为止,将Groovy和Java类混合在一起时,您必须小心地选择正确的编译顺序来编译这两种类型的类,并且当循环依赖项跨越两种语言时,您可能会遇到“鸡和蛋问题。 幸运的是,对于Groovy 1.5,情况已不再如此,并且得益于屡获殊荣的Java IDE IntelliJ IDEA的制造商JetBrains的贡献,“联合”编译器可用,您可以在其中一起编译Groovy和Java源代码。无需考虑类之间的依赖关系。
如果要从命令行使用联合编译器,则可以照常调用groovyc命令,但是指定-j标志将启用联合编译:
groovyc *.groovy *.java -j -Jsource=1.4 -Jtarget=1.4
为了将参数传递给基础javac命令,可以在标志前面加上J前缀。 您还可以通过Ant或Maven构建文件中的Ant任务使用联合编译器:
<taskdef name="groovyc"
classname="org.codehaus.groovy.ant.Groovyc"
classpathref="my.classpath"/>
<groovyc
srcdir="${mainSourceDirectory}"
destdir="${mainClassesDirectory}"
classpathref="my.classpath"
jointCompilationOptions="-j -Jsource=1.4 -Jtarget=1.4" />
Groovy的Maven插件
对于Maven用户,Codehaus上还托管了一个功能齐全的Maven插件,该插件可让您构建Java / Groovy应用程序:编译Groovy和Java代码,从JavaDoc标记生成文档,甚至还可以让您编写自己的Maven。 Groovy中的插件。 还有一个Maven原型可以更快地引导您的Groovy项目。 有关更多信息,您可以查看插件的文档: http : //mojo.codehaus.org/groovy/index.html
GroovyDoc文档工具
作为Java开发人员,您习惯于通过JavaDoc标记在类,接口,字段或方法的注释中记录代码。 在Groovy中,您还可以在注释中使用这样的标记,并使它们被称为GroovyDoc的工具使用,以为所有Groovy类生成等效的JavaDoc文档。
您可以定义一个Ant任务,然后使用它来生成文档,如下所示:
<taskdef name="groovydoc"
classname="org.codehaus.groovy.ant.Groovydoc">
<classpath>
<path path="${mainClassesDirectory}"/>
<path refid="compilePath"/>
</classpath>
</taskdef>
<groovydoc
destdir="${docsDirectory}/gapi"
sourcepath="${mainSourceDirectory}"
packagenames="**.*" use="true"
windowtitle="Groovydoc" private="false"/>
新的交互式外壳和Swing控制台
Groovy发行版始终包含两个不同的外壳:命令行外壳和Swing控制台。 命令行外壳Groovysh在与用户的交互方面从未如此友好:每当您要执行一条语句时,都必须在每个语句之后键入'go'或'execute',这样它才能被执行。 为了快速制作原型或使用一些新的API,每次都要输入“ go”非常麻烦。 自从新的交互式shell诞生以来,这种情况在Groovy 1.5中已经改变。 无需再输入“ go”。
这个新的Shell具有一些增强功能,例如使用提供ANSI着色的JLine库,命令的制表符完成,行编辑功能。 您可以使用不同的脚本缓冲区,记住已导入的类,加载现有脚本,将当前脚本保存到文件,浏览历史记录等。有关受支持功能的详细说明,请查看文档 。
命令行外壳并不是唯一需要注意的命令行外壳,Swing控制台也得到了改进,它具有新的工具栏,高级撤消功能,可以增加或减小字体大小,突出显示语法的功能。 控制台已进行了大量抛光。
IntelliJ IDEA JetGroovy插件
在本节末尾,我将通过提及JetGroovy插件来保留最佳的工具支持:这是一个免费的开源IntelliJ IDEA插件,专用于Groovy和Grails的支持。 该插件是由JetBrains自己开发的,并且为语言和Web框架提供了无与伦比的支持。
列出Groovy支持的一些可用功能:
- 所有语法的语法高亮显示 ,以及针对无法识别的类型的不同警告,或未知的静态类型信息有助于发现潜在错误的方法。
- 能够运行用Groovy编写的Groovy类,脚本和JUnit测试用例 。
- 调试器 :您可以逐步调试Java和Groovy代码,设置断点,显示变量,当前堆栈等。
- 联合编译器:编译器将Groovy和Java类一起编译 ,并且能够解析两种语言之间的依赖关系。
- 包,类,属性,字段,变量,方法,关键字的代码完成 ,甚至对Swing UI构建器的特定支持。
- 高级课程搜索和查找用法。
- 重构 :您也可以在Java中爱上大多数常用的重构,并且可以跨Java和Groovy进行重构,例如“环绕”,引入,内联或重命名变量,重命名包,类,方法和字段。 。
- 导入优化和代码格式 。
- 结构视图:对您的课程有鸟瞰图。
归根结底,考虑到IntelliJ IDEA中提供的交互和支持水平,您甚至不会注意到您是用Groovy还是Java开发类。 如果您正在考虑在Java项目中添加一定数量的Groovy,或者计划开发Grails应用程序,则绝对是一个要安装的插件。
可以在JetBrains网站上找到更多信息。
尽管我仅提到IntelliJ IDEA的插件,但是对于您的Groovy开发,您不必改变习惯。 您还可以使用由IBM Project Zero开发人员定期改进的Eclipse插件,或者使用NetBeans中 Sun对Groovy和Grails的全新支持。
性能提升
与以前的版本相比,Groovy的新版本具有新功能,带来了显着的性能改进以及更低的内存消耗。 在衡量所有测试套件持续时间的非正式基准测试中,我们注意到与Beta版本的Groovy 1.5相比,速度提高了15%至45%-与目前较旧的Groovy 1.0相比,可以肯定会有更高的数字。 尚待开发更正式的基准,但这些数字也得到了使用Groovy编写其保单风险计算引擎的业务规则的保险公司的开发人员的确认,以及另一家在高度并行计算机上进行了多次测试的公司的开发人员的确认。 。 总体而言,Groovy 1.5在大多数情况下应该更快,更精简。 您的里程可能会因您自己使用Groovy的情况而异。
增强的动态功能
通过Groovy与Grails项目之间的共生关系,在Grails的心脏中成熟之后,Groovy引入了新的动态功能。
Groovy是一种动态语言:简单来说,它意味着诸如方法分派之类的某些事情在运行时发生,而不是像Java和其他语言那样在编译时发生。 有一个特定的运行时系统,称为MOP(代表元对象协议),负责分配逻辑。 幸运的是,此运行时系统足够开放,因此人们可以进入系统并更改其通常的行为。 对于每个Java类和每个Groovy实例,都有一个关联的元类,该元类表示对象的这种运行时行为。 Groovy通过扩展一些基类来定义自定义元类,从而提供了多种与MOP进行交互的方法,但是由于Grails项目的贡献,Groovy提供了一种groovier类型的元类:expando元类。
同样,代码示例更容易帮助我们理解概念。 在以下示例中,msg String实例具有一个元类,我们可以通过metaClass属性对其进行访问。 然后,我们更改String类的元类,以添加一个称为String类的新方法,以具有toUpperCase()方法的快捷方式。 为此,我们将闭包分配给元类的up属性,该属性在我们为其分配闭包时创建。 此闭包不带任何参数(因此为什么要以箭头开头),因此我们在闭包的委托上调用toUpperCase()方法,这是一个特殊的闭包变量,它代表真实对象(此处为String实例)。
def msg = "Hello!"
println msg.metaClass
String.metaClass.up = { -> delegate.toUpperCase() }
assert "HELLO!" == msg.up()
通过此元类,您可以查询可用的方法和/或属性:
// print all the methods
obj.metaClass.methods.each { println it.name }
// print all the properties
obj.metaClass.properties.each { println it.name }
您甚至可以检查某个方法或属性是否可用,其粒度比通过任何instanceof检查要精细:
def msg = 'Hello!'
if (msg.metaClass.respondsTo(msg, 'toUpperCase')) {
println msg.toUpperCase()
}
if (msg.metaClass.hasProperty(msg, 'bytes')) {
println foo.bytes.encodeBase64()
}
这些机制在Grails Web框架中广泛用于创建动态查找器:在大多数情况下都不需要DAO,因为您可以在Book域类上调用findByTitle()动态方法。 通过元类,Grails使用此类方法自动装饰域类。 此外,如果该方法尚不存在,则将在首次使用时创建并缓存该方法。 这可以通过其他高级钩子来完成,如下所述。
除了我们到目前为止所看到的那些示例之外,expando元类还提供了一些互补的钩子。 可以将四种其他方法添加到expando元类中:
- invokeMethod()可让您拦截所有方法调用,
- 而methodMissing()将在没有其他方法的情况下在最后的手段上被调用。
- get / setProperty()拦截对所有属性的访问,
- 当找不到属性时调用propertyMissing() 。
与先前版本的Groovy相比,使用expando元类,自定义应用程序类型的行为变得更加容易,并且可以节省宝贵的开发时间。 显然,并不是每个人都需要使用这些技术,但是在许多情况下,当您想应用一些AOP(面向方面的技术)来装饰类时,以及在简化和提高业务代码可读性的情况下,它们都可以派上用场。通过删除一些不必要的样板代码来简化应用程序。
在类固醇上摇摆
Groovy项目有机会拥有一支由才华横溢的Swing开发人员组成的团队,他们会努力工作以增强Groovy的功能,以在Swing中构建用户界面。 在Groovy中构建Swing UI的基本基础是SwingBuilder类:在您的源代码的语法级别上,您可以直观地看到Swing组件如何相互嵌套。 Groovy网站上的一个简单示例显示了如何简单地创建一个小GUI:
import groovy.swing.SwingBuilder
import java.awt.BorderLayout
import groovy.swing.SwingBuilder
import java.awt.BorderLayout as BL
def swing = new SwingBuilder()
count = 0
def textlabel
def frame = swing.frame(title:'Frame', size:[300,300]) {
borderLayout()
textlabel = label(text:"Clicked ${count} time(s).",
constraints: BL.NORTH)
button(text:'Click Me',
actionPerformed: {count++; textlabel.text =
"Clicked ${count} time(s)."; println "clicked"},
constraints:BorderLayout.SOUTH)
}
frame.pack()
frame.show()
在新颖性方面,Swing构建器概念已扩展为提供定制组件工厂。 还有其他模块,默认情况下未与Groovy捆绑在一起,这些模块将JIDE或SwingX项目中的Swing组件集成到常规的Swing构建器代码中。
尽管本主题值得一读,但我只列出该版本中的其他一些改进,例如bind()方法。 受bean绑定JSR(JSR-295)的启发,您可以轻松地将组件或bean绑定在一起,以使它们对彼此所做的更改做出React。 在以下示例中,将根据滑块组件的值来更改按钮的插入大小:
import groovy.swing.SwingBuilder
import java.awt.Insets
swing = new SwingBuilder()
frame = swing.frame {
vbox {
slider(id: 'slider', value:5)
button('Big Button?!', margin:
bind(source: slider,
sourceProperty:'value',
converter: { [it, it, it, it] as Insets }))
}
}
frame.pack()
frame.size = [frame.width + 200, frame.height + 200]
frame.show()
将组件绑定在一起是构建用户界面时的常见任务,通过此绑定机制已简化了此任务。 还可以使用其他一些自动绑定选项,但是再次写一篇专门的文章可能会更好。
在其他新功能和值得注意的功能中,添加了一些方便的新方法,这些方法利用闭包来调用臭名昭著的SwingUtilities类并启动新线程:edt()将调用invokeAndWait() ,而doLater()将调用invokeLater() ,和doOutside()只会在新线程中启动闭包。 不再有丑陋的匿名内部类:只需通过这些快捷方式使用闭包即可!
最后但并非最不重要的一点是,由于SwingBuilder上的build()方法,将视图的描述及其关联的行为逻辑分开从未如此简单。 您可以创建一个仅包含视图的单独脚本,而组件之间的交互或绑定在主类中,从而在MVC模型中进行更清晰的分离。
摘要
在本文中,概述了新的和值得注意的功能,但是我们几乎没有刮过新版Groovy的表面。 主要亮点主要是围绕Java 5的新功能,例如注释,枚举或泛型:它使Groovy完美地能够与Spring,Hibernate或JPA等企业框架很好地无缝集成。 通过语法的改进和增强的动态功能,Groovy将允许您通过创建嵌入式的特定于域的语言来自定义业务逻辑,从而可以轻松地在应用程序的扩展点进行集成。 通过投入到工具支持中的工作,开发人员的经验有了显着的进步,这不再是采用Groovy的障碍。 总体而言,对于Groovy 1.5,简化开发人员生活的目标从未实现过,并且Groovy绝对应该成为所有Java开发人员工具箱的一部分。
groovy 2.5.4