每次都覆写toString,始终重写

发布时间:2019-10-04  栏目:编程  评论:0 Comments

Tips《Effective Java, Third
Edition》一书英文版已经出版,这本书的第二版想必很多人都读过,号称Java四大名著之一,不过第二版2009年出版,到现在已经将近8年的时间,但随着Java
6,7,8,甚至9的发布,Java语言发生了深刻的变化。在这里第一时间翻译成中文版。供大家学习分享之用。书中的源代码地址:
9 API中的,所以JDK 最好下载 JDK 9以上的版本。但是Java 9
只是一个过渡版本,所以建议安装JDK 10。

虽然Object提供了一个toString方法的实现,但是它返回的字符串通常不是你的类使用者想看的。它包含了类名,
紧随着一个“at”符号(@)和无符号的哈希码的十六进制表示,比如PhoneNumber@163b91。toString的通用协定说,返回的字符串应该是“一个简洁但是易于读懂的信息表达”。虽然PhoneNumber@163b91是简洁和容易阅读,这可能有争议,但对比707-867-5309,
它不是非常有信息的。toString协定继续说,“所有子类覆写这个方法,这是推荐的”。好的建议,确实!

图片 1Effective
Java, Third Edition

虽然这不是像遵从equals和hashCode协定(条目10和11)那么关键,提供一个好的toString实现使得你的类使用更加愉悦,而且使得使用这个类的系统更加容易调试。当一个对象被传递到println、printf、字符串连接操作子、断言,或者被调试工具打印,toString方法自动被调用。即使你从未调用对象的toString,其他的人会。比如,一个组件,有一个对你对象的引用,可能在日志错误信息里面包含对象的字符串表示。如果你没有覆写toSTring,信息可能几乎没有用。

虽然Object类提供了toString方法的实现,但它返回的字符串通常不是你的类的用户想要看到的。
它由类名后跟一个“at”符号和哈希码的无符号十六进制表示组成,例如PhoneNumber@163b91
toString的通用约定要求,返回的字符串应该是“一个简洁但内容丰富的表示,对人们来说是很容易阅读的”。虽然可以认为PhoneNumber@163b91简洁易读,但相比于707-867-5309,但并不是很丰富
。 toString通用约定“建议所有的子类重写这个方法”。好的建议,的确如此!

如果你已经为PhoneNumber提供了一个好的toString方法,产生一个有用的诊断信息就像如下一样容易:

虽然它并不像遵守equals和hashCode约定那样重要,但是提供一个良好的toString实现使你的类更易于使用,并对使用此类的系统更易于调试。当对象被传递到println、printf、字符串连接操作符或断言,或者由调试器打印时,toString方法会自动被调用。即使你从不调用对象上的toString,其他人也可以。例如,对对象有引用的组件可能包含在日志错误消息中对象的字符串表示。如果未能重写toString,则消息可能是无用的。

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

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

程序员以这种方式产生诊断信息,不管你是否覆写了toString,但是除非你这么做,否则信息将不会有用。提供一个好的toString方法的益处延伸到类的实例之外,至包含这些实例引用的对象,特别是数据集。当打印一个映射{Jenny=PhoneNumber@163b91}或者{Jenny=707-867-5309},你愿意看见哪一个呢?

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

在实际使用时,toString方法应该返回包含在对象中的所有感兴趣的信息,就像在电话号码例子所示。如果一个对象很大,或者如果它包含状态不有助于字符串表示,这是不实际的。这种情况下,toString应该返回概要,比如Manhattan
residential phone directory (1487536
listings)或者Thread[main,5,main]。理想上,字符串是不言自明的。(Thread例子没有达到这个目标)。在它的字符串表示中,未能包含一个对象所有感兴趣的信息的一个非常讨厌的坏处,在于检测失败报告看上去如下:

程序员将以这种方式生成诊断消息,不管你是否重写toString,但是除非你这样做,否则这些消息将不会有用。
提供一个很好的toString方法的好处不仅包括类的实例,同样有益于包含实例引用的对象,特别是集合。
打印map
对象时你会看到哪一个,{Jenny=PhoneNumber@163b91}还是{Jenny=707-867-5309}?

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

实际上,toString方法应该返回对象中包含的所有需要关注的信息,如电话号码示例中所示。
如果对象很大或者包含不利于字符串表示的状态,这是不切实际的。
在这种情况下,toString应该返回一个摘要,如
Manhattan residential phone directory (1487536 listings)或线程[main,5,main]
理想情况下,字符串应该是不言自明的(线程示例并没有遵守这点)。
如果未能将所有对象的值得关注的信息包含在字符串表示中,则会导致一个特别烦人的处罚:测试失败报告如下所示:

当实现toString方法时,你不得不做的一个重要的决定是,在文档上是否要指定返回值的格式。你可以为值类型这么做,比如电话号码或者矩阵,这也是推荐的。指定格式的优势在于它是作为一个标准的、清楚的和人工可读的对象表示。这个表示可以用作输入和输出,而且在持久化人工可读的数据对象,比如CSV文件。如果你指定了格式,那么通常提供一个匹配的静态工厂或者构造子是一个好主意,如此,程序员可以容易地在对象和它的字符串表示之间来回转换。在Java平台库中的许多值类采用这个方法,包括BigInteger、BigDecimal和原始装箱类的大多数。

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

指定toString返回值的格式的缺点在于,一旦你指定了它,你需要终生坚持它,假设你的类被广泛使用。程序员将编写代码解析这个表示,来生成它,而且把它陷入到持久化数据里面。如果你在未来的发布中改变了这个表示,你讲破坏他们的代码和数据,他们会怒吼。通过不选择指定一个格式,你保持了在后续发布中添加信息或者改进格式的灵活性。

实现toString方法时,必须做出的一个重要决定是:在文档中指定返回值的格式。
建议你对值类进行此操作,例如电话号码或矩阵类。
指定格式的好处是它可以作为标准的,明确的,可读的对象表示。
这种表示形式可以用于输入、输出以及持久化可读性的数据对象,如CSV文件。
如果指定了格式,通常提供一个匹配的静态工厂或构造方法,是个好主意,所以程序员可以轻松地在对象和字符串表示之间来回转换。
Java平台类库中的许多值类都采用了这种方法,包括BigInteger,BigDecimal和大部分基本类型包装类。

无论你是否指定格式,你应该清楚地文档化你的意图。如果你指定了这个格式,那么你应该精确地这么做。比如,下面是一个toString方法,与条目11的PhoneNumber类相配:

留下评论

网站地图xml地图