- 浏览: 5171663 次
- 性别:
- 来自: 天津
博客专栏
-
实战 Groovy
浏览量:29395
文章分类
- 全部博客 (639)
- 代码之谜 (6)
- JavaScript quirks (5)
- 程序员 (92)
- Java (93)
- BT编程 (7)
- html/css (64)
- Groovy&Grails (42)
- Android (20)
- C/C++ (5)
- PHP/Perl/Python (46)
- 经典文章 (51)
- CodeIgniter (14)
- JQuery (10)
- 笑话 (4)
- 其他 (32)
- javascript (69)
- 云计算 (0)
- html5 (7)
- 面试 (8)
- google (3)
- nosql (2)
- nodejs (11)
- go (5)
- erlang (1)
- 小常识 (3)
- 冷知识 (5)
- database (4)
- web (12)
- 架构 (12)
- Exception (0)
最新评论
-
jqw1992:
https://www.chromefor.com/libra ...
[福利] 开发者必备的 Chrome 插件——ChromeSnifferPlus -
litjerk:
初步算了一下,目前最最精简的Win98版是5M,他5个小时多敲 ...
让人目瞪口呆的三位世界级电脑大师 -
379855529:
。。似乎重点没说NIO啊,前面基础只是铺垫的很好的,可是我要的 ...
Java NIO与IO的详细区别(通俗篇) -
springmvc_springjpa:
spring mvc demo教程源代码下载,地址:http: ...
一步步开发 Spring MVC 应用 -
匡建武:
Good
四个程序员的一天
Java™ 取消了操作符重载,但是新兴的 Groovy 又使之浮出水面。在实战 Groovy 定期连载的“Groovy 每日应用”的最后一期中,请随着 Andrew Glover 介绍的三类可重载操作符,重新寻回自己多年来失去的东西。
许多以前使用 C++ 的开发人员会怀念操作符重载,例如 +
和 -
。虽然它们很方便,但是被覆盖的操作符的多态实质会造成混淆,所以操作符重载在 Java 语言中被取消了。这个限制的好处是清晰:Java 开发人员不必猜想两个对象上的 +
是把它们加在一起还是把一个对象附加到另一个对象上。不好的地方则是丧失了一个有价值的简写形式。
|
现在,期望放任自由的 Groovy 把这个简写形式带回来!在 实战 Groovy 的这一期中,我将介绍 Groovy 对操作符即时多态(也称为操作符重载)的支持。正如 C++ 开发人员会告诉您的,这个东西既方便又有趣,虽然必须小心谨慎才能接近。
我把 Groovy 的可重载操作符分成三个逻辑组: 比较操作符、算术类操作符和数组类操作符。这几组只涵盖了普通 Java 编程中可用操作符的一个子集。例如,像 &
和 ^
这样的逻辑操作符目前在 Groovy 中不可用。
表 1 显示了 Groovy 中可用的三组可重载操作符:
1. | 比较操作符对应着普通的 Java equals 和 compareTo 实现 |
2. | Java 的算术类操作符,例如 + 、- 和 *
|
3. | 数组存取类操作符 []
|
比较操作符对应着 Java 语言中的 equals
和 compareTo
实现,通常用作集合中排序的快捷方式。表 2 显示了 Groovy 的两个比较操作符:
a == b | a.equals(b) |
a <=> b | a.compareTo(b) |
操作符 ==
是 Java 语言中表示对象相等的简写形式,但是不代表对象引用的相等。换句话说,放在两个对象之间的 Groovy 的 ==
意味着它们相同,因为它们的属性是相等的,虽然每个对象指向独立的引用。
根据 Javadoc,Java 语言的 compareTo()
方法返回负整数、0 或正整数,代表对象小于、等于或大于指定对象。因为这个方法能够返回三个值,所以 Groovy 用四个附加值扩充了 <=>
语法,如表 3 所示:
a > b | 如果 a.compareTo(b) 返回的值大于 0,那么这个条件为 true
|
a >= b | 如果 a.compareTo(b) 返回的值大于等于 0,那么这个条件为 true
|
a < b | 如果 a.compareTo(b) 小于 0,那么这个条件为 true
|
a <= b | 如果 a.compareTo(b) 小于等于 0,那么这个条件为 true
|
还记得我最初在“感受 Groovy”一文中定义的磁盘友好的 LavaLamp
类么,它还在“实战 Groovy: Groovy 的腾飞”一文中充当迁移到新的 JSR 语法的示例,我要再次使用这个类来演示使用比较操作符的一些漂亮的技巧。
在清单 1 中,我通过实现普通 Java 的 equals()
方法(以及它的可恶伙伴 hashCode
)增强了 LavaLamp
类。另外,我让 LavaLamp
实现了 Java 语言的 Comparable
接口并创建了 compareTo()
方法的实现:
package com.vanward.groovy import org.apache.commons.lang.builder.CompareToBuilder import org.apache.commons.lang.builder.EqualsBuilder import org.apache.commons.lang.builder.HashCodeBuilder import org.apache.commons.lang.builder.ToStringBuilder class LavaLamp implements Comparable{ @Property model @Property baseColor @Property liquidColor @Property lavaColor def String toString() { return new ToStringBuilder(this). append(this.model). append(this.baseColor). append(this.liquidColor). append(this.lavaColor). toString() } def boolean equals(obj) { if (!(obj instanceof LavaLamp)) { return false } LavaLamp rhs = (LavaLamp) obj return new EqualsBuilder(). append(this.model, rhs.model). append(this.baseColor, rhs.baseColor). append(this.liquidColor, rhs.liquidColor). append(this.lavaColor, rhs.lavaColor). isEquals() } def int hashCode() { return new HashCodeBuilder(17, 37). append(this.model). append(this.baseColor). append(this.liquidColor). append(this.lavaColor). toHashCode() } def int compareTo(obj) { LavaLamp lmp = (LavaLamp)obj return new CompareToBuilder(). append(lmp.model, this.model). append(lmp.lavaColor, this.lavaColor). append(lmp.baseColor, this.baseColor). append(lmp.liquidColor, this.liquidColor). toComparison() } } |
注: 因为我是重用狂,所以我的这些实现严重依赖 Jakarta 的 Commons Lang 项目(甚至 toString()
方法也是如此!)如果您还在自己编写 equals()
方法,那么您可能想花一分钟来签出这个库。(请参阅 参考资料 。)
在清单 2 中,可以看到我在清单 1 中设置的操作符重载的效果。我创建了五个 LavaLamp
实例(没错,我们在开 party!)并用 Groovy 的比较操作符来区分它们:
lamp1 = new LavaLamp(model:"1341", baseColor:"Black", liquidColor:"Clear", lavaColor:"Red") lamp2 = new LavaLamp(model:"1341", baseColor:"Blue", liquidColor:"Clear", lavaColor:"Red") lamp3 = new LavaLamp(model:"1341", baseColor:"Black", liquidColor:"Clear", lavaColor:"Blue") lamp4 = new LavaLamp(model:"1342", baseColor:"Blue", liquidColor:"Clear", lavaColor:"DarkGreen") lamp5 = new LavaLamp(model:"1342", baseColor:"Blue", liquidColor:"Clear", lavaColor:"DarkGreen") println lamp1 <=> lamp2 // 1 println lamp1 <=> lamp3 // -1 println lamp1 < lamp3 // true println lamp4 <=> lamp5 // 0 assert lamp4 == lamp5 assert lamp3 != lamp4 |
注意 lamp4
和 lamp5
是如何相同的,以及其他对象之间具有什么样的细微差异。因为 lamp1
的 baseColor
是 Black
而lamp2
的 baseColor
是 Blue
,所以 <=>
返回 1
(black 中的 a 比 blue 中的 u 靠前)。类似地,lamp3
的 lavaColor
是 Blue
,而 lamp1
的是 Red
。因为条件 lamp1 <=> lamp3
返回 -1
,所以语句 lamp1 < lamp3
返回 true
。llamp4
和 llamp5
是相等的,所以 <=>
返回 0
。
而且,可以看到 ==
也适用于对象相等。lamp4
和 lamp5
是同一对象。当然,我应当用 assert lamp4.equals(lamp5)
证实这一点,但是 ==
更快捷!
现在,如果真的想测试对象引用相等该怎么办?显然,我不能用 ==
,但是 Groovy 为这类事情提供了 is()
方法,如清单 3 所示:
lamp6 = new LavaLamp(model:"1344", baseColor:"Black", liquidColor:"Clear", lavaColor:"Purple") lamp7 = lamp6 assert lamp7.is(lamp6) |
在清单 3 中可以看出,lamp6
和 lamp7
是相同的引用,所以 is
返回 true
。
顺便说一句,如果感觉迷糊,不要惊讶:使用重载操作符差不多让 Groovy 语言退步了。但是我认为是有趣的。
Groovy 支持以下算术类操作符的重载:
a + b | a.plus(b) |
a - b | a.minus(b) |
a * b | a.multiply(b) |
a / b | a.divide(b) |
a++ or ++a | a.next() |
a-- or --a | a.previous() |
a << b | a.leftShift(b) |
您可能已经注意到 Groovy 中的 +
操作符已经在几个不同的领域重载了,特别是在用于集合的时候。您是否想过,这怎么可能?或者至少想过对自己的类能否这么做?现在我们来看答案。
还记得“在 Java 应用程序中加一些 Groovy 进来”一文中的 Song
类么?我们来看看当我创建一个 JukeBox 对象来播放 Song
时,发生了什么。在清单 4 中,我将忽略实际播放 MP3 的细节,把重点放在从JukeBox
添加和减去 Song
上:
package com.vanward.groovy import com.vanward.groovy.Song class JukeBox { def songs JukeBox(){ songs = [] } def plus(song){ this.songs << song } def minus(song){ def val = this.songs.lastIndexOf(song) this.songs.remove(val) } def printPlayList(){ songs.each{ song -> println "${song.getTitle()}" } } } |
通过实现 plus()
和 minus()
方法,我重载了 +
和 -
,现在可以把它们用在脚本中了。清单 5 演示了它们从播放列表添加和减少歌曲的行为:
sng1 = new Song("SpanishEyes.mp3") sng2 = new Song("RaceWithDevilSpanishHighway.mp3") sng3 = new Song("Nena.mp3") jbox = new JukeBox() jbox + sng1 jbox + sng2 jbox + sng3 jbox.printPlayList() //prints Spanish Eyes, Race with the Devil.., Nena jbox - sng2 jbox.printPlayList() //prints Spanish Eyes, Nena |
继续进行这个重载的 主题,您可能注意到,在表 3 中有一个可以重载的算术类操作符 <<
,它恰好也为 Groovy 的集合重载。在集合的情况下,<<
覆盖后的作用像普通的 Java add()
方法一样,把值添加到集合的尾部(这与 Ruby 也很相似)。在清单 6 中,可以看到当我模拟这个行为,允许 JukeBox
的用户通过 <<
操作符以及 +
操作符添加 Song
时发生的情况:
def leftShift(song){ this.plus(song) } |
在清单 6 中,我实现了 leftShift
方法,它调用 plus
方法添加 Song
到播放列表。清单 7 显示了 <<
操作符的效果:
jbox << sng2 //re-adds Race with the Devil... |
可以看出,Groovy 的算术类重载操作符不仅能负重,而且能做得很快!
Groovy 支持重载标准的 Java 数组存取语法 []
,如表 4 所示:
a[b] | a.getAt(b) |
a[b] = c | a.putAt(b, c) |
数组存取语法很好地映射到集合,所以我在清单 8 中更新了 JukeBox
类,把两种情况都做了重载:
def getAt(position){ return songs[position] } def putAt(position, song){ songs[position] = song } |
现在我实现了 getAt
和 putAt
,我可以使用 []
语法了,如清单 9 所示:
println jbox[0] //prints Spanish Eyes jbox[0] = sng2 //placed Race w/the Devil in first slot println jbox[0] //prints Race w/the Devil |
一旦掌握了操作符重载的概念和它在 Groovy 中的实现,就可以看到许多日常的 Java 对象已经 被 Groovy 的作者做了改进。
例如,Character
类支持 compareTo()
,如清单 10 所示:
def a = Character.valueOf('a' as char) def b = Character.valueOf('b' as char) def c = Character.valueOf('c' as char) def g = Character.valueOf('g' as char) println a < b //prints true println g < c //prints false |
同样,StringBuffer
可以用 <<
操作符进行添加,如清单 11 所示:
def strbuf = new StringBuffer() strbuf.append("Error message: ") strbuf << "NullPointerException on line ..." println strbuf.toString() //prints Error message: NullPointerException on line ... |
最后,清单 12 表示 Date
可以通过 +
和 -
来操纵。
def today = new Date() println today //prints Tue Oct 11 21:15:21 EDT 2005 println "tomorrow: " + (today + 1) //Wed Oct 12 21:15:21 EDT 2005 println "yesterday: " + (today - 1) //Mon Oct 10 21:15:21 EDT 2005 |
可以看到,操作符的即时多态,或操作符重载,对于我们来说,如果小心使用和记录,会非常强大。但是,要当心不要滥用这个特性。如果决定覆盖一个操作符去做一些非常规的事情,请一定要清楚地记录下您的工作。对 Groovy 类进行改进,支持重载非常简单。小心应对并记录所做的工作,对于由此而来的方便的简写形式来说,代价非常公道。
发表评论
-
实战 Groovy: 用 Groovy 打造服务器端
2010-07-10 11:07 2730Groovlet 和 GroovyServer P ... -
实战 Groovy: 用 Groovy 生成器作标记
2010-07-10 11:07 2069Groovy 生成器让您能够利用诸如 Swing 这样 ... -
实战 Groovy: for each 剖析
2010-07-10 11:07 18202在这一期的 实战 Groovy 中,Scott Davi ... -
实战 Groovy: 用 Groovy 进行 Ant 脚本编程
2010-07-10 11:07 2061Ant 和 Maven 两者在构建处理工具的世界中占统 ... -
实战 Groovy: 在 Java 应用程序中加一些 Groovy 进来
2010-07-10 11:06 4333您有没有想过在自己相对复杂的 Java 程序中嵌入 G ... -
实战 Groovy: Groovy 的腾飞
2010-07-10 11:06 2192随着 Groovy JSR-1(及其后续发行版本)的发 ... -
实战 Groovy: 用 curry 过的闭包进行函数式编程
2010-07-10 11:06 3256在 Groovy 中处处都是闭包,Groovy 闭包惟 ... -
实战 Groovy: 关于 MOP 和迷你语言
2010-07-10 11:06 2072将耳朵贴到地上仔细听 —— MOP 正在前进!了解一下 ... -
实战 Groovy: 用 Groovy 更迅速地对 Java 代码进行单元测试
2010-07-10 11:06 2306不久以前,developerWor ... -
实战 Groovy: 构建和解析 XML
2010-07-10 11:05 7122通过本文,您将了解 ... -
实战 Groovy: 用 Groovy 进行 JDBC 编程
2010-07-10 11:05 5195这个月,随着 Andrew G ... -
实战 Groovy: 使用 Groovy 模板进行 MVC 编程
2010-07-10 11:04 2923视图是 MVC 编程的一个重要部分,而 MVC 编程本 ... -
实战 Groovy: 用 Groovy 减少代码冗余
2010-07-10 11:04 2062Groovy 简洁的语法将 ... -
实战 Groovy: Groovy:Java 程序员的 DSL
2010-07-10 11:04 3083Groovy 专家 Scott Davis 将重新开始 ... -
精通 Grails: 构建您的第一个 Grails 应用程序
2010-07-06 09:37 1651Java™ 程序员不需要 ... -
Grails 部署
2010-07-06 09:36 6075部署 Grails可以使用很多种方式来部署,每一种 ... -
Grails 脚手架
2010-07-05 08:20 4080脚手架 根据指定的领域类,脚手架为你自动生成 ... -
Grails Grails 与 Hibernate
2010-07-05 08:19 2703Grails 与 Hibernate 如果 GOR ... -
Grails Grails和 Spring
2010-07-05 08:19 7915Grails和 Spring 这一节适合于高级用户 ... -
Grails Web服务
2010-07-05 08:19 3959Web服务 Web服务就是让你的web应用提供一套 ...
相关推荐
本书《Groovy in Action》英文第二版为读者提供了全面的Groovy实战指南。在前言中,我们看到James Gosling,Java的创始人,对此书给予高度评价,这也反映了Groovy在动态语言特性以及在敏捷开发方面的表现。 本书...
Groovy还提供了重载操作符的能力,允许程序员为基本数据类型或自定义类型编写新的操作符行为。在字符串处理方面,Groovy引入了GString,这种字符串可以包含表达式,能够在运行时被评估。同时,Groovy还引入了对正则...
2. **groovy解惑**:随着对Groovy的深入学习,可能会遇到一些迷惑或者难题,例如Groovy的元编程特性、GDK(Groovy Development Kit)中的扩展方法和操作符重载等。这部分资料可能专注于解决这些疑惑,提供解释和实战...
1. **词法分析**:词法分析器(也称为扫描器或分词器)将源代码分解成一系列的标记(tokens),这些标记代表了源代码的基本单元,如关键字、标识符、操作符等。 2. **语法分析**:ANTLR4生成的解析器使用这些标记来...
Starter POMs是一组方便的依赖描述符,用于在项目中引入所需的Spring Boot相关依赖。例如,spring-boot-starter-data-jpa包含了使用Spring Data JPA所需要的所有依赖项。 **3. 外部化配置** Spring Boot支持从多种...
3. **可变参数**:允许方法接受一个可变数量的参数,通过`...`操作符实现,例如`public void printNumbers(int... numbers)`。 4. **静态导入**:允许将类中的静态成员直接引入到当前作用域,减少冗余的类名引用,...
- **封装**: 封装的意义,以及如何通过访问控制修饰符(public/private/protected)实现封装。 **LESSON7: Programming with Abstract Classes and Interfaces** - **抽象类**: 抽象类的特点、应用场景以及如何定义...
4. **扩展函数与属性**:Kotlin允许为已有类添加功能,无需继承或使用装饰者模式,这对于Android开发中的UI组件操作尤其有用。 5. **协程(Coroutines)**:讲解Kotlin的协程概念,它是解决Android应用中异步问题的...
今天我们将深入探讨一个广受欢迎的图片加载库——Universal Image Loader(简称UIL),并结合"6Universal_Image_Loader_Demo"项目进行实战解析。 Universal Image Loader是一个强大的、灵活的开源图片加载、缓存和...
Maven实战 3. 一个简单的Maven项目 3.1. 简介 3.1.1. 下载本章的例子 3.2. 创建一个简单的项目 3.3. 构建一个简单的项目 3.4. 简单的项目对象模型 (Project Object Model) 3.5. 核心概念 3.5.1. Maven...