【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 平台上,產生的 |

Java 學習之路





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強烈反對所致.
(https://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() 上頭的話.