
【Guava 教學】(4)實作 toString、equals 與 hashCode 的幫手
如果你建立了一個 public class Point { public Integer x; public Integer y; public Point(Integer x, Integer y) { this.x = x; this.y = y; } } 並且用它來產生了一些 List<Point> points = Arrays.asList(new Point(1, 1), new Point(2, 2)); out.println(points); 執行結果只會顯示[guavademo.Point@139a55, guavademo.Point@1db9742]這種資訊,真的是沒什麼用,這就是為什麼你要定義 public class Point { ... @Override public String toString() { return "Point{" + "x=" + x + ", y=" + y + '}'; } } IDE 幫你產生是不錯,不過要自己寫這些就不怎麼高興了,而且讀來也不怎麼好讀,改用 public class Point { ... @Override public String toString() { return String.format("Point{x=%d, y=%d}", x, y); } } 不過自己做字串格式化終究還是蠻麻煩的,你可以改用 Guava 的 import com.google.common.base.Objects; public class Point { ... @Override public String toString() { return Objects.toStringHelper(this) .add("x", x) .add("y", y) .toString(); } } 除了產生 import com.google.common.base.Objects; public class Point { public Integer x; public Integer y; public Point(Integer x, Integer y) { this.x = x; this.y = y; } @Override public String toString() { return Objects.toStringHelper(this) .add("x", x) .add("y", y) .toString(); } @Override public boolean equals(Object that) { if(that instanceof Point) { Point p = (Point) that; return x.equals(p.x) && y.equals(p.y); } return false; } } 不過,這個 ... public class Point { ... @Override public boolean equals(Object that) { if(that instanceof Point) { Point p = (Point) that; return Objects.equal(x, p.x) && Objects.equal(y, p.y); } return false; } }
... public static boolean equal(@Nullable Object a, @Nullable Object b) { return a == b || (a != null && a.equals(b)); } ... 你可以使用目前的 Point p1 = new Point(1, 1); Point p2 = new Point(1, 1); Set<Point> pSet = new HashSet<>(); pSet.add(p1); out.println(pSet.contains(p2)); // 可能顯示 false 如果上例結果顯示
以 在重新定義 IDE 產生的 ... @Override public int hashCode() { return 41 * (41 + x) + y; } ... 如果使用 JDK7,那麼可以用 ... @Override public int hashCode() { return Objects.hashCode(x, y); } ... 實際上目前版本的 Guava 只是用了 JDK5 就有的 ... public static int hashCode(@Nullable Object... objects) { return Arrays.hashCode(objects); } ... 所以實際上,你應該看看 再次做剛剛的測試就會得到 Point p1 = new Point(1, 1); Point p2 = new Point(1, 1); Set<Point> pSet = new HashSet<>(); pSet.add(p1); out.println(pSet.contains(p2)); // true 如果你沒瞭解過 當然,你也可以讓 IDE 結合 Guava 來產生 新版的 NetBeans 本身如果在 JDK7 平台上,產生的 |
Valentino Young
07/07
觀察Guava API 至今, 不斷地看到對應apache commons有相同或相似的,
而在Guava之中的命名與呼叫設計方面更加賞心悅目...的各種工具,
每每引誘出 "下一個project開始就把 com.apache.common 換成 com.google.common 吧" 的念頭.
然而在 ToStringHelper(ToStringBuilder) 這方面, 卻看不到 ToStringStyle 的Guava版...
到Guava的forum上看了一下, 似乎是Guava的project member強烈反對所致.
(http://code.google.com/p/guava-libraries/issues/detail?id=400)
他們表示是基於 "簡潔, 結果一致" 原則設計Guava API,
並且認為95%以上的需求不會在乎Object#toString()所輸出的格式...
Object#toString() 本是給人類眼球方便的成分遠大於給電腦CPU方便的一種安排,
在多年的實務之中, 為了全文檢索與文字差異比較...等等目的, 陸續設計出幾個ToStringStyle,
並且由精心排版過的 Object#toString() 輸出,
才得以找出的BUG, 得以察覺的ISSUE, 得以產生的靈感...等等也不計其數.
不過回頭反省一下, 也是因為歷來承接下來的projects總是設計概念混亂, 程式碼混濁不明...等先天缺陷,
才會走利用 Object#toString() 來改善系統的旁門左道吧.
最近的重構工作碰到了 "若非幾萬個檔案一一打開起來用肉眼確認,
用各種檢索方式都無法區分那些legacy codes在用的究竟是物件本身?還是在用物件的toString()?" 的處境,
才體會到當 "Java基本語法/各類函式庫之中往往 someObject.toString() 可直接用 someObject 取代" 的安排,
與 "讓toString()直接產生業務邏輯所要的結果" 的習慣,
兩者長期交互作用之後, 會製造出多麼令人頭痛的問題...
回頭看guava forum上issue 400的建議與否決, 覺得 ToStringStyle 的問題在於他的出發點跟名稱.
將物件內容以pretty format呈現出來的需求仍會存在,
ToStringStyle之類的概念是很不錯的 --- 只要別用在 Object#toString() 上頭的話.