Java 為避免 C++ 風格運算子多載造成的語意模糊,刻意移除此功能。Groovy 作為 JVM 動態語言,重新引入運算子多載(即時多態),提供簡潔語法糖,但需謹慎使用並完善文件化。
| 類別 | 運算子 | 對應方法 | 說明 |
|---|---|---|---|
| 比較運算子 | == | equals() | 物件內容相等(非參考相等) |
<=> | compareTo() | 三向比較,衍生出 > >= < <= | |
| 算術運算子 | + | plus() | 加法/串接 |
- | minus() | 減法/移除 | |
* | multiply() | 乘法 | |
/ | divide() | 除法 | |
++/-- | next()/previous() | 遞增/遞減 | |
<< | leftShift() | 左移/集合加入 | |
| 陣列存取運算子 | a[b] | getAt(b) | 索引讀取 |
a[b]=c | putAt(b,c) | 索引寫入 |
實作 Comparable 介面與 equals()/hashCode(),利用 Apache Commons Lang 建構器簡化:
class LavaLamp implements Comparable {
@Property String model, baseColor, liquidColor, lavaColor
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()
}
int hashCode() {
return new HashCodeBuilder(17, 37)
.append(model).append(baseColor)
.append(liquidColor).append(lavaColor)
.toHashCode()
}
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()
}
}
測試比較行為:
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 (Black < Blue)
println lamp1 <=> lamp3 // -1 (Red < Blue)
println lamp1 < lamp3 // true
println lamp4 <=> lamp5 // 0 (相等)
assert lamp4 == lamp5 // 內容相等
assert lamp3 != lamp4 // 內容不等
參考相等性測試使用 is() 方法:
lamp6 = new LavaLamp(model:"1344", baseColor:"Black", liquidColor:"Clear", lavaColor:"Purple")
lamp7 = lamp6
assert lamp7.is(lamp6) // 同一物件參考
class JukeBox {
def songs = []
def plus(song) { songs << song }
def minus(song) { songs.remove(songs.lastIndexOf(song)) }
def leftShift(song) { plus(song) } // 支援 << 語法
def printPlayList() { songs.each { println it.title } }
}
// 使用範例
jbox = new JukeBox()
jbox + sng1; jbox + sng2; jbox + sng3
jbox.printPlayList() // Spanish Eyes, Race with the Devil..., Nena
jbox - sng2
jbox.printPlayList() // Spanish Eyes, Nena
jbox << sng2 // 重新加入
class JukeBox {
// ... 前述程式碼
def getAt(pos) { songs[pos] }
def putAt(pos, song) { songs[pos] = song }
}
// 使用範例
println jbox[0] // Spanish Eyes
jbox[0] = sng2
println jbox[0] // Race w/the Devil
Groovy 已為常用 JDK 類別實作運算子多載:
<=> 比較<< 追加字串+/- 日期運算// Character 比較
def a = 'a' as char, b = 'b' as char
println a < b // true
// StringBuffer 追加
def strbuf = new StringBuffer()
strbuf.append("Error: ")
strbuf << "NullPointerException"
println strbuf.toString()
// 日期運算
def today = new Date()
println today + 1 // 明天
println today - 1 // 昨天
運算子多載威力強大,但濫用將導致程式碼難以理解。核心原則:
Groovy 讓我們在保持 JVM 相容性的前提下,重獲表達力豐富的運算子語法,關鍵在於審慎抉擇使用時機。