Item 12: Always override toString(始终覆盖 toString 方法)

While Object provides an implementation of the toString method, the string that it returns is generally not what the user of your class wants to see. It consists of(由…组成) the class name followed by an “at” sign (@) and the unsigned hexadecimal representation of the hash code, for example,PhoneNumber@163b91. The general contract for toString says that the returned string should be “a concise but informative representation that is easy for a person to read.” While it could be argued that PhoneNumber@163b91 is concise and easy to read, it isn’t very informative when compared to 707-867-5309. The toString contract goes on to say, “It is recommended that all subclasses override this method.” Good advice, indeed!

虽然 Object 提供 toString 方法的实现,但它返回的字符串通常不是类的用户希望看到的。它由后跟「at」符号(@)的类名和 hash 代码的无符号十六进制表示(例如 PhoneNumber@163b91)组成。toString 的通用约定是这么描述的,返回的字符串应该是「简洁但信息丰富的表示,易于阅读」。虽然有人认为 PhoneNumber@163b91 简洁易懂,但与 707-867-5309 相比,它的信息量并不大。toString 约定接着描述,「建议所有子类覆盖此方法。」好建议,确实!

While it isn’t as critical(adj.至关重要的) as obeying the equals and hashCode contracts (Items 10 and 11), providing a good toString implementation makes your class much more pleasant to use and makes systems using the class easier to debug. The toString method is automatically invoked when an object is passed to println, printf, the string concatenation(n.连结) operator, or assert, or is printed by a debugger. Even if you never call toString on an object, others may. For example, a component that has a reference to your object may include the string representation of the object in a logged error message. If you fail to override toString, the message may be all but useless.

虽然它不如遵守 equals 和 hashCode 约定(Item-10Item-11)那么重要,但是 提供一个好的 toString 实现(能)使类更易于使用,使用该类的系统(也)更易于调试。 当对象被传递给 println、printf、字符串连接操作符或断言或由调试器打印时,将自动调用 toString 方法。即使你从来没有调用 toString 对象,其他人也可能(使用)。例如,有对象引用的组件可以在日志错误消息中包含对象的字符串表示。如果你未能覆盖 toString,则该消息可能完全无用。

If you’ve provided a good toString method for PhoneNumber,generating a useful diagnostic message is as easy as this:

如果你已经为 PhoneNumber 提供了一个好的 toString 方法,那么生成一个有用的诊断消息就像这样简单:

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

Programmers will generate diagnostic messages in this fashion whether or not you override toString, but the messages won’t be useful unless you do. The benefits of providing a good toString method extend beyond instances of the class to objects containing references to these instances, especially collections.Which would you rather see when printing a map,{Jenny=PhoneNumber@163b91} or {Jenny=707-867-5309}?

无论你是否覆盖 toString,程序员都会以这种方式生成诊断消息,但是除非你(覆盖 toString),否则这些消息不会有用。提供好的 toString 方法的好处不仅仅是将类的实例扩展到包含对这些实例的引用的对象,特别是集合。在打印 map 时,你更愿意看到哪个,{Jenny=PhoneNumber@163b91} 还是 {Jenny=707-867-5309}?

When practical, the toString method should return all of the interesting information contained in the object, as shown in the phone number example. It is impractical if the object is large or if it contains state that is not conducive to string representation. Under these circumstances,toString should return a summary such as Manhattan residential phone directory (1487536 listings) or Thread[main,5,main]. Ideally, the string should be self-explanatory. (The Thread example flunks this test.) A particularly annoying penalty for failing to include all of an object’s interesting information in its string representation is test failure reports that look like this:

当实际使用时,toString 方法应该返回对象中包含的所有有趣信息, 如电话号码示例所示。如果对象很大,或者包含不利于字符串表示的状态,那么这种方法是不切实际的。在这种情况下,toString 应该返回一个摘要,例如曼哈顿住宅电话目录(1487536 号清单)或 Thread[main,5,main]。理想情况下,字符串应该是不言自明的。(线程示例未能通过此测试。)如果没有在字符串表示中包含所有对象的有趣信息,那么一个特别恼人的惩罚就是测试失败报告,如下所示:

Assertion failure: expected {abc, 123}, but was {abc, 123}.

One important decision you’ll have to make when implementing a toString method is whether to specify the format of the return value in the documentation. It is recommended that you do this for value classes, such as phone number or matrix. The advantage of specifying the format is that it serves as a standard, unambiguous, human-readable representation of the object. This representation can be used for input and output and in persistent human-readable data objects, such as CSV files. If you specify the format, it’s usually a good idea to provide a matching static factory or constructor so programmers can easily translate back and forth between the object and its string representation. This approach is taken by many value classes in the Java platform libraries, including BigInteger, BigDecimal, and most of the boxed primitive classes.

在实现 toString 方法时,你必须做的一个重要决定是是否在文档中指定返回值的格式。建议你针对值类(如电话号码或矩阵)这样做。指定格式的优点是,它可以作为对象的标准的、明确的、人类可读的表示。这种表示可以用于输入和输出,也可以用于持久的人类可读数据对象,比如 CSV 文件。如果指定了格式,提供一个匹配的静态工厂或构造函数通常是一个好主意,这样程序员就可以轻松地在对象及其字符串表示之间来回转换。Java 库中的许多值类都采用这种方法,包括 BigInteger、BigDecimal 和大多数包装类。

The disadvantage of specifying the format of the toString return value is that once you’ve specified it, you’re stuck with it for life, assuming your class is widely used. Programmers will write code to parse the representation, to generate it, and to embed it into persistent data. If you change the representation in a future release, you’ll break their code and data, and they will yowl. By choosing not to specify a format, you preserve the flexibility to add information or improve the format in a subsequent release.

指定 toString 返回值的格式的缺点是,一旦指定了它,就会终生使用它,假设你的类被广泛使用。程序员将编写代码来解析表示、生成表示并将其嵌入持久数据中。如果你在将来的版本中更改了表示形式,你将破坏它们的代码和数据,它们将发出大量的消息。通过选择不指定格式,你可以保留在后续版本中添加信息或改进格式的灵活性。

Whether or not you decide to specify the format, you should clearly document your intentions. If you specify the format, you should do so precisely. For example, here’s a toString method to go with the PhoneNumber class in Item 11:

无论你是否决定指定格式,你都应该清楚地记录你的意图。 如果指定了格式,则应该精确地指定格式。例如,这里有一个 toString 方法用于Item-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);
}

If you decide not to specify a format, the documentation comment should read something like 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() { ... }

After reading this comment, programmers who produce code or persistent data that depends on the details of the format will have no one but themselves to blame when the format is changed.

在阅读了这篇文档注释之后,当格式被更改时,生成依赖于格式细节的代码或持久数据的程序员将只能怪他们自己。

Whether or not you specify the format, provide programmatic access to the information contained in the value returned by toString. For example, the PhoneNumber class should contain accessors for the area code, prefix, and line number. If you fail to do this, you force programmers who need this information to parse the string. Besides reducing performance and making unnecessary work for programmers, this process is error-prone and results in fragile systems that break if you change the format. By failing to provide accessors, you turn the string format into a de facto API, even if you’ve specified that it’s subject to change.

无论你是否指定了格式,都要 提供对 toString 返回值中包含的信息的程序性访问。 例如,PhoneNumber 类应该包含区域代码、前缀和行号的访问器。如果做不到这一点,就会迫使需要这些信息的程序员解析字符串。除了降低性能和使程序员不必要的工作之外,这个过程很容易出错,并且会导致脆弱的系统在你更改格式时崩溃。由于没有提供访问器,你可以将字符串格式转换为事实上的 API,即使你已经指定了它可能会发生更改。

It makes no sense to write a toString method in a static utility class (Item 4). Nor should you write a toString method in most enum types (Item 34) because Java provides a perfectly good one for you. You should, however, write a toString method in any abstract class whose subclasses share a common string representation. For example, the toString methods on most collection implementations are inherited from the abstract collection classes.

在静态实用程序类中编写 toString 方法是没有意义的(Item-4),在大多数 enum 类型中也不应该编写 toString 方法(Item-34),因为 Java 为你提供了一个非常好的方法。但是,你应该在任何抽象类中编写 toString 方法,该类的子类共享公共的字符串表示形式。例如,大多数集合实现上的 toString 方法都继承自抽象集合类。

Google’s open source AutoValue facility, discussed in Item 10, will generate a toString method for you, as will most IDEs. These methods are great for telling you the contents of each field but aren’t specialized to the meaning of the class. So, for example, it would be inappropriate to use an automatically generated toString method for our PhoneNumber class (as phone numbers have a standard string representation), but it would be perfectly acceptable for our Potion class. That said, an automatically generated toString method is far preferable to the one inherited from Object, which tells you nothing about an object’s value.

谷歌的开放源码自动值工具(在 Item-10 中讨论)将为你生成 toString 方法,大多数 IDE 也是如此。这些方法可以很好地告诉你每个字段的内容,但并不专门针对类的含义。因此,例如,对于 PhoneNumber 类使用自动生成的 toString 方法是不合适的(因为电话号码具有标准的字符串表示形式),但是对于 Potion 类来说它是完全可以接受的。也就是说,一个自动生成的 toString 方法要比从对象继承的方法好得多,对象继承的方法不会告诉你对象的值。

To recap, override Object’s toString implementation in every instantiable class you write, unless a superclass has already done so. It makes classes much more pleasant to use and aids in debugging. The toString method should return a concise, useful description of the object, in an aesthetically pleasing format.

回顾一下,在你编写的每个实例化类中覆盖对象的 toString 实现,除非超类已经这样做了。它使类更易于使用,并有助于调试。toString 方法应该以美观的格式返回对象的简明、有用的描述。


Back to contents of the chapter(返回章节目录)

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,445评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,889评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,047评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,760评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,745评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,638评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,011评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,669评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,923评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,655评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,740评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,406评论 4 320
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,995评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,961评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,023评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,483评论 2 342

推荐阅读更多精彩内容

  • **2014真题Directions:Read the following text. Choose the be...
    又是夜半惊坐起阅读 9,363评论 0 23
  • rljs by sennchi Timeline of History Part One The Cognitiv...
    sennchi阅读 7,283评论 0 10
  • 胸大肌 部位:胸前上部皮下。起点:锁骨内侧半、胸骨前面和第1~6肋软骨以及腹直肌鞘前壁上部。止点:肱骨大结节嵴。功...
    醉杀楚天白阅读 756评论 0 0
  • 这几张照片是在拍《寸心笑傲》这组照片的时候顺便拍的。 我们之前是在城墙下边的一段拍摄人像,拍到一半的时候下了大雨,...
    囡囡nannanxbk阅读 661评论 8 7
  • ES6中的class 简介 JS语法中起始是原生不怎么支持面向对象的,所有的面向对象起源均通过一个构造函数,这一点...
    人失格阅读 623评论 0 3