程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> 關於JAVA >> Groovy允許在Java 平台上重載操作符

Groovy允許在Java 平台上重載操作符

編輯:關於JAVA

Java? 取消了操作符重載,但是新興的 Groovy 又使之浮出水面。在實戰 Groovy 定期連載的“Groovy 每日應用”的最後一期中,請隨著 Andrew Glover 介紹的三類可重載操作符,重新尋回自己多年來失去的東西。

  

  許多以前使用 C++ 的開發人員會懷念操作符重載,例如 + 和 -。雖然它們很方便,但是被覆蓋的操作符的多態實質會造成混淆,所以操作符重載在 Java 語言中被取消了。這個限制的好處是清晰:Java 開發人員不必猜想兩個對象上的 + 是把它們加在一起還是把一個對象附加到另一個對象上。不好的地方則是喪失了一個有價值的簡寫形式。

  

  把任何一個工具用於開發實踐的關鍵是,知道什麼時候使用它而什麼時候把它留在工具箱中。腳本語言(或動態語言)可以是工具箱中極為強大的工具,但是只有在恰當地應用到適當的場景中才是這樣。為了這個目標,實戰 Groovy 是一系列文章,專門介紹 Groovy 的實際應用,並教給您什麼時候、如何成功地應用它們。

  

  現在,期望放任自由的 Groovy 把這個簡寫形式帶回來!在 實戰 Groovy 的這一期中,我將介紹 Groovy 對操作符即時多態(也稱為操作符重載)的支持。正如 C++ 開發人員會告訴您的,這個東西既方便又有趣,雖然必須小心謹慎才能接近。

  

  三類可重載操作符

  

  我把 Groovy 的可重載操作符分成三個邏輯組: 比較操作符、算術類操作符和數組類操作符。這幾組只涵蓋了普通 Java 編程中可用操作符的一個子集。例如,像 & 和 ^ 這樣的邏輯操作符目前在 Groovy 中不可用。

  

  表 1 顯示了 Groovy 中可用的三組可重載操作符:

  

  表 1. Groovy 的可重載操作符

  

  1. 比較操作符對應著普通的 Java equals 和 compareTo 實現

  2. Java 的算術類操作符,例如 +、- 和 *

  3. 數組存取類操作符 []

  

  比較操作符

  

  比較操作符對應著 Java 語言中的 equals 和 compareTo 實現,通常用作集合中排序的快捷方式。表 2 顯示了 Groovy 的兩個比較操作符:

  

  表 2. 比較操作符

  

  操作符   方法

  a == b   a.equals(b)

  a <=> b  a.compareTo(b)

  

  操作符 == 是 Java 語言中表示對象相等的簡寫形式,但是不代表對象引用的相等。換句話說,放在兩個對象之間的 Groovy 的 == 意味著它們相同,因為它們的屬性是相等的,雖然每個對象指向獨立的引用。

  

  根據 Javadoc,Java 語言的 compareTo() 方法返回負整數、0 或正整數,代表對象小於、等於或大於指定對象。因為這個方法能夠返回三個值,所以 Groovy 用四個附加值擴充了 <=> 語法,如表 3 所示:

  

  表 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() 方法的實現:

  

  清單 1. LavaLamp 歸來!

  

  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 的比較操作符來區分它們:

  

  清單 2. 比較操作符的效果

  

  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 所示:

  

  清單 3. is() 的作用!

  

  lamp6 = new LavaLamp(model:"1344", baseColor:"Black",

  liquidColor:"Clear", lavaColor:"Purple")

  

  lamp7 = lamp6

  assert lamp7.is(lamp6)

  

  在清單 3 中可以看出,lamp6 和 lamp7 是相同的引用,所以 is 返回 true。

  

  順便說一句,如果感覺迷糊,不要驚訝:使用重載操作符差不多讓 Groovy 語言退步了。但是我認為是有趣的。

  

  算術類操作符

  

  Groovy 支持以下算術類操作符的重載:

  

  表 3. 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 上:

  

  清單 4. Jukebox

  

  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 演示了它們從播放列表添加和減少歌曲的行為:

  

  清單 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 時發生的情況:

  

  清單 6. 左移音樂

  

  def leftShift(song){

  this.plus(song)

  }

  

  在清單 6 中,我實現了 leftShift 方法,它調用 plus 方法添加 Song 到播放列表。清單 7 顯示了 << 操作符的效果:

  

  清單 7. 比賽進行中

  

  jbox << sng2 //re-adds Race with the Devil...

  

  可以看出,Groovy 的算術類重載操作符不僅能負重,而且能做得很快!

  

  數組類操作符

  

  Groovy 支持重載標准的 Java 數組存取語法 [],如表 4 所示:

  

  表 4. 數組操作符

  

  操作符   方法

  a[b]    a.getAt(b)

  a[b] = c  a.putAt(b, c)

  

  數組存取語法很好地映射到集合,所以我在清單 8 中更新了 JukeBox 類,把兩種情況都做了重載:

  

  清單 8. Music 重載

  

  def getAt(position){

  return songs[position]

  }

  

  def putAt(position, song){

  songs[position] = song

  }

  

  現在我實現了 getAt 和 putAt,我可以使用 [] 語法了,如清單 9 所示:

  

  清單 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 化的 JDK 方法

  

  一旦掌握了操作符重載的概念和它在 Groovy 中的實現,就可以看到許多日常的 Java 對象已經 被 Groovy 的作者做了改進。

  

  例如,Character 類支持 compareTo(),如清單 10 所示:

  

  清單 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 所示:

  

  清單 11. 緩沖區中的字符串

  

  def strbuf = new StringBuffer()

  strbuf.append("Error message: ")

  strbuf << "NullPointerException on line ..."

  

  println strbuf.toString() //prints Error message: NullPointerException on line ...

  

  最後,清單 12 表示 Date 可以通過 + 和 - 來操縱。

  

  清單 12. 是哪一天?

  

  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 類進行改進,支持重載非常簡單。小心應對並記錄所做的工作,對於由此而來的方便的簡寫形式來說,代價非常公道。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved