1. 程式人生 > >Effective Java 第三版讀書筆記——條款12:總是重寫 toString 方法

Effective Java 第三版讀書筆記——條款12:總是重寫 toString 方法

雖然 Object 類提供了 toString 方法的實現,但它返回的字串通常不是類的使用者想要看到的。它由類名後跟一個 “ at ” 符號(@)和雜湊碼的無符號十六進位制表示組成,例如 [email protected]。toString 的通用約定要求,返回的字串應該是“一個簡潔但內容豐富的表示,對人們來說是很容易閱讀的”。提供一個良好的 toString 實現使你的類更易於使用,並且使用此類的系統更易於除錯。當物件被傳遞到 println、printf、字串連線操作符、斷言,或者由偵錯程式列印時,toString 方法會自動被呼叫。

如果為 PhoneNumber 提供了一個很好的 toString 方法,那麼生成一個有用的診斷訊息就像下面這樣簡單:

System.out.println("Failed to connect to " + phoneNumber);

實際上,toString 方法應該返回物件中包含的所有需要關注的資訊,如電話號碼示例中所示。如果物件很大或者包含不利於字串表示的狀態,包含所有資訊就是不切實際的。在這種情況下,toString 應該返回一個摘要,如 Manhattan residential phone directory (1487536 listings) 或執行緒 [main,5,main]。理想情況下,字串應該是自解釋的(執行緒示例並沒有遵守這點)。

實現 toString 方法時,必須做出的一個重要決定是:是否在文件中指定返回值的格式。建議你對值類(value clsses)進行此操作,例如電話號碼或矩陣類。指定格式的好處是它可以作為標準的,明確的,可讀的物件表示。這種表示形式可以用於輸入、輸出以及持久化可讀性的資料物件,如 CSV 檔案。如果指定了格式,提供一個匹配的靜態工廠或構造方法通常是個好主意,這使程式設計師可以輕鬆地在物件和字串表示之間來回轉換。Java 平臺類庫中的許多值類都採用了這種方法,包括 BigInteger、BigDecimal 和大部分基本型別包裝類。

指定 toString 返回值的格式的缺點是:假設你的類被廣泛使用,那麼一旦指定了格式,這個格式就會被一直使用,導致可擴充套件性很差。程式設計師將編寫程式碼來解析表示式,生成它,並將其嵌入到持久資料中。如果在將來的版本中更改了格式的表示,那麼會破壞他們的程式碼和資料。但通過選擇不指定格式,就可以保留在後續版本中新增資訊或改進格式的靈活性。

無論是否決定指定格式,你都應該清楚地在文件中表明你的意圖。如果你指定了格式,就要明確地表示出來。例如,這裡有一個條款 11 中使用的 PhoneNumber 類的 toString 方法:

/**
 * 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);
}

如果你決定不指定格式,那麼文件註釋應該是這樣的:

/**
 * 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 這三個屬性的 getter 方法。如果不這樣做,就會強迫需要這些資訊的程式設計師來解析字串。這降低了程式效能,而且程式設計師做了不必要的工作,整個過程還很容易出錯。

在靜態工具類(條款 4)中重寫 toString 方法是沒有意義的。你也不應該在大多數列舉型別(條款 34)中寫一個 toString 方法,因為 Java 為你提供了一個非常好的方法。但是,如果一個抽象類的子類有相同的字串表示形式,你應該在這個抽象類中重寫 toString 方法。例如,大多數集合實現中的 toString 方法都是從抽象集合類中繼承的。

回顧一下,除非父類已經這樣做了,否則在每個可例項化的類中重寫 toString 方法。它使類的使用更加舒適,還可以協助除錯。toString 方法應該以一種美觀的格式返回物件的簡明有用的描述。