Tips
《Effective Java, Third Edition》一書英文版已經出版,這本書的第二版想必不少人都讀過,號稱Java四大名著之一,不過第二版2009年出版,到如今已經將近8年的時間,但隨着Java 6,7,8,甚至9的發佈,Java語言發生了深入的變化。
在這裏第一時間翻譯成中文版。供你們學習分享之用。git
雖然Object類提供了toString方法的實現,但它返回的字符串一般不是你的類的用戶想要看到的。 它由類名後跟一個「at」符號(@)和哈希碼的無符號十六進制表示組成,例如PhoneNumber@163b91
。 toString的通用約定要求,返回的字符串應該是「一個簡潔但內容豐富的表示,對人們來講是很容易閱讀的」。雖然能夠認爲PhoneNumber@163b91
簡潔易讀,但相比於707-867-5309
,但並非很豐富 。 toString通用約定「建議全部的子類重寫這個方法」。好的建議,的確如此!程序員
雖然它並不像遵照equals和hashCode約定那樣重要(條目 10和11),可是提供一個良好的toString實現使你的類更易於使用,並對使用此類的系統更易於調試。當對象被傳遞到println、printf、字符串鏈接操做符或斷言,或者由調試器打印時,toString方法會自動被調用。即便你從不調用對象上的toString,其餘人也能夠。例如,對對象有引用的組件可能包含在日誌錯誤消息中對象的字符串表示。若是未能重寫toString,則消息多是無用的。編程
若是爲PhoneNumber
提供了一個很好的toString方法,那麼生成一個有用的診斷消息就像下面這樣簡單:api
System.out.println("Failed to connect to " + phoneNumber);
程序員將以這種方式生成診斷消息,無論你是否重寫toString,可是除非你這樣作,不然這些消息將不會有用。 提供一個很好的toString方法的好處不只包括類的實例,一樣有益於包含實例引用的對象,特別是集合。 打印map 對象時你會看到哪個,{Jenny=PhoneNumber@163b91}
仍是{Jenny=707-867-5309}
?ide
實際上,toString方法應該返回對象中包含的全部須要關注的信息,如電話號碼示例中所示。 若是對象很大或者包含不利於字符串表示的狀態,這是不切實際的。 在這種狀況下,toString應該返回一個摘要,如 Manhattan residential phone directory (1487536 listings)
或線程[main,5,main]
。 理想狀況下,字符串應該是不言自明的(線程示例並無遵照這點)。 若是未能將全部對象的值得關注的信息包含在字符串表示中,則會致使一個特別煩人的處罰:測試失敗報告以下所示:工具
Assertion failure: expected {abc, 123}, but was {abc, 123}.
實現toString方法時,必須作出的一個重要決定是:在文檔中指定返回值的格式。 建議你對值類進行此操做,例如電話號碼或矩陣類。 指定格式的好處是它能夠做爲標準的,明確的,可讀的對象表示。 這種表示形式能夠用於輸入、輸出以及持久化可讀性的數據對象,如CSV文件。 若是指定了格式,一般提供一個匹配的靜態工廠或構造方法,是個好主意,因此程序員能夠輕鬆地在對象和字符串表示之間來回轉換。 Java平臺類庫中的許多值類都採用了這種方法,包括BigInteger,BigDecimal和大部分基本類型包裝類。性能
指定toString返回值的格式的缺點是,假設你的類被普遍使用,一旦指定了格式,就會終身使用。程序員將編寫代碼來解析表達式,生成它,並將其嵌入到持久數據中。若是在未來的版本中更改了格式的表示,那麼會破壞他們的代碼和數據,而且還會抱怨。但經過選擇不指定格式,就能夠保留在後續版本中添加信息或改進格式的靈活性。學習
不管是否決定指定格式,你都應該清楚地在文檔中代表你的意圖。若是指定了格式,則應該這樣作。例如,這裏有一個toString方法,該方法在條目 11中使用PhoneNumber
類:測試
/** * Returns the string representation of this phone number. * The string consists of twelve characters whose format is * "XXX-YYY-ZZZZ", where XXX is the area code, YYY is the * prefix, and ZZZZ is the line number. Each of the capital * letters represents a single decimal digit. * * If any of the three parts of this phone number is too small * to fill up its field, the field is padded with leading zeros. * For example, if the value of the line number is 123, the last * four characters of the string representation will be "0123". */ @Override public String toString() { return String.format("%03d-%03d-%04d", areaCode, prefix, lineNum); }
若是你決定不指定格式,那麼文檔註釋應該是這樣的:this
/** * Returns a brief description of this potion. The exact details * of the representation are unspecified and subject to change, * but the following may be regarded as typical: * * "[Potion #9: type=love, smell=turpentine, look=india ink]" */ @Override public String toString() { ... }
在閱讀了這條註釋以後,那些生成依賴於格式細節的代碼或持久化數據的程序員,在這種格式發生改變的時候,只能怪他們本身。
不管是否指定格式,均可以經過編程方式訪問toString返回的值中包含的信息。 例如,PhoneNumber
類應該包含 areaCode, prefix, lineNum這三個屬性。 若是不這樣作,就會強迫程序員須要這些信息來解析字符串。 除了下降性能和程序員作沒必要要的工做以外,這個過程很容易出錯,若是改變格式就會中斷,並致使脆弱的系統。 因爲未能提供訪問器,即便已指定格式可能會更改,也能夠將字符串格式轉換爲事實上的API。
在靜態工具類(條目 4)中編寫toString方法是沒有意義的。 你也不該該在大多數枚舉類型(條目 34)中寫一個toString方法,由於Java爲你提供了一個很是好的方法。 可是,你應該在任何抽象類中定義toString方法,該類的子類共享一個公共字符串表示形式。 例如,大多數集合實現上的toString方法都是從抽象集合類繼承的。
Google的開放源代碼AutoValue工具在條目 10中討論過,它爲你生成一個toString方法,就像大多數IDE工具同樣。 這些方法很是適合告訴你每一個屬性的內容,但並非專門針對類的含義。 所以,例如,爲咱們的PhoneNumber
類使用自動生成的toString方法是不合適的(由於電話號碼具備標準的字符串表示形式),可是對於咱們的Potion
類來講,這是徹底能夠接受的。 也就是說,自動生成的toString方法比從Object繼承的方法要好得多,它不會告訴你對象的值。
回顧一下,除非父類已經這樣作了,不然在每一個實例化的類中重寫Object的toString實現。 它使得類更加溫馨地使用和協助調試。 toString方法應該以一種美觀的格式返回對象的簡明有用的描述。